本文深入解析JavaScript中变量声明与定义的区别,详细阐述全局作用域与局部作用域的交互规则。重点讲解未使用var声明变量的隐式全局行为、函数嵌套中的变量遮蔽现象,以及JavaScript缺乏块级作用域导致的变量提升特性,通过代码示例说明undefined值的产生原因。
变量声明与定义的区别
在探讨编程语言时,理解变量的“声明”与“定义”是两个基础但易混淆的概念。
对此,我的理解如下:
“声明”变量仅指在代码中告知解释器该变量的存在,而“定义”变量则不仅包含声明,还涉及具体的赋值操作。
例如:
代码如下:
var name;//只是声明
var num = 11;//声明,并且赋值,即定义了
var password = "yangjiang";//声明,并且赋值,即定义了 基于上述概念,以下是关于变量作用域的几点总结:
JavaScript变量作用域规则
变量的作用域主要分为全局作用域和局部作用域。需要注意的是,如果尝试读取一个尚未声明的变量,JavaScript引擎会抛出错误。
第一点:当局部变量或函数参数使用var关键字声明,且其名称与某个全局变量相同时,
该局部变量会有效地隐藏(遮蔽)同名的全局变量。
例如:
代码如下:
var scope1 = "global";//var修饰
function checksScope(){
var scope1 = "local";//var修饰
document.write(scope1);
}checksScope();//local 第二点:如果尝试给一个未使用var关键字声明的变量赋值,
该变量会被隐式创建为全局变量,即使它是在函数内部使用的(前提是函数已执行)。注意,这不适用于函数嵌套情形下的局部遮蔽。
例如:
代码如下:
scope2 = "globalAAAAA";//没有使用var修饰(js会默认将其声明为全局变量)
function checkScopeA(){
scope2 = "localAAAAA";//没有使用var修饰(js会默认将其声明为全局变量)
document.write("<br/>"+scope2);
myscope = "myLocalAAAAA";//没有使用var修饰(js会默认将其声明为全局变量)
document.write(","+myscope);
}
checkScopeA();//localAAAAA,myLocalAAAAA *A
document.write("<br/>"+scope2);//localAAAAA *B
document.write("<br/>"+myscope);//myLocalAAAAA *C 如果将上面的例子中的 *A处的代码注释掉,
例如:
代码如下:
scope2 = "globalAAAAA";//没有使用var修饰(js会默认将其声明为全局变量)
function checkScopeA(){
scope2 = "localAAAAA";//没有使用var修饰(js会默认将其声明为全局变量)
document.write("<br/>"+scope2);
myscope = "myLocalAAAAA";//没有使用var修饰(js会默认将其声明为全局变量)
document.write(","+myscope);
}
//checkScopeA(); *A
document.write("<br/>"+scope2);//globalAAAAA *B
document.write("<br/>"+myscope);//发生错误 *C 因为函数checkScopeA没有执行,所以 *B处输出为globalAAAAA;
因为函数checkScopeA没有执行,所以变量myscope没有声明,如果尝试读取一个未声明的变量,会发生错误。
函数嵌套与局部作用域
第三点:
在JavaScript中,函数定义支持嵌套。由于每个函数都拥有独立的局部作用域,因此可能出现多层局部作用域嵌套的情况。
例如:
代码如下:
var scope3 = "global scope"; //定义了一个全局变量
function checkScopeB(){
var scope3 = "local scope"; //定义了一个局部变量,覆盖了全局变量scope3
function nested(){
var scope3 = "nested scope"; //在函数的函数的内部,定义了一个局部变量
document.write("<br/>"+scope3); //nested scope
}
nested();
}
checkScopeB();//nested scope 缺乏块级作用域与变量提升
第四点:
在JavaScript中,不存在传统意义上的块级作用域。在函数中声明的所有变量,无论其声明位置如何,在整个函数体内均可见(即有声明)。
同理,在函数中定义的所有变量,无论定义位置如何,在整个函数体内均有效(即有定义)。
例如:
代码如下:
function test(o){//根据以上的说明:此函数中的i,j,k三个变量的作用域是相同的。
var i = 0; //变量 i 在整个函数中都有定义
if(typeof o == "object"){
var j = 0 ; //变量 j 在整个函数中都有定义,而不仅仅是在 if 语句块
for(var k=0;k<10;k++){//变量 k 在整个函数中都有定义,而不仅仅是在 if 语句块
document.write("<br/>k的值为:"+k);
}
document.write("<br/>for循环外k的值:"+k);//此时的 k 仍然被定义了,k=10
}
document.write("<br/>j的值:"+j); //变量 j 被声明了,但可能没有被初始化 因为可能往函数中 传入的参数 不是对象 ,if语句块不会执行
} 下面通过两种方式调用此函数:
方式一:传入对象
test({});//输出结果:上例中的注释
方式二:什么都不传
test();//输出结果:j的值:undefined
想不明白的是,在第二种方式中的输出结果,为什么会是 undefined。我当时猜的是:j的值:0
后来,这本书上面说:
由于局部变量在整个函数体内都是有声明(或定义)的,这就意味着在整个函数体中都隐藏了同名的全局
变量。虽然 局部变量在整个函数体内中都是有声明(或定义)的,但是在执行var语句之前,它是不会被初始化的。
这样的话,上面的方式二调用的输出结果,就比较好解释了,由于变量j在整个函数中都有定义,而又由于传入函数的参数为空,所以函数体中的if语句不会执行,从而使得j的值为undefined.(这是我参照上面书上说的那句话的理解)
下面的例子,更好的说明:
代码如下:
var sssss = "全局变量";
function f(){
document.write("<br/>"+sssss);//输出:undefined 而不是输出"全局变量"
var sssss = "局部变量";
document.write("<br/>"+sssss);//输出:局部变量
}