作用域即变量和函数的可访问范围。在js中,作用域包括全局作用域和局部作用域。
1 变量作用域
在函数体内,局部变量的优先级高于同名的全局变量。如果在函数内声明的一个局部变量或者函数参数中带有的变量和全局变量重名,那么全局变量就被局部变量所遮盖。
声明局部变量必须用var语句。
2 函数作用域(function scope)和声明提前(hoisting)
变量在声明它们的函数体以及这个函数体嵌套的任意函数体内都是有定义的。
1 var scope="global"; 2 function f(){ 3 console.log(scope);//输出“undefined”而不是”global“ 4 var scope ="local"; 5 console.log(scope);//输出local 6 }
javascript函数里声明的所有变量(不涉及赋值)都被”提前“至函数体的顶部,同时变量初始化留在原来的位置,这个特性被称为声明提前(hoisting)。所以上述过程等价于
1 var scope="global"; 2 function f(){ 3 var scope; 4 console.log(scope);//输出“undefined”而不是”global“ 5 scope ="local"; 6 console.log(scope);//输出local 7 }
在函数体内,局部变脸scope将覆盖全局变量中定义的scope,但并未对其赋值,所以输出为undefined.
3 作用域链
当声明一个javascript全局变量时,实际上是定义了一个全局对象的一个属性。局部变量是和函数调用相关的某个对象的属性。
每一段javascript代码(全局代码或函数)都有一个与之关联的作用域链(scope chain)。这个作用域链是一个对象列表或链表,这组对象定义了这段代码”作用域中“的变量。当javascript需要查找变量X时(即变量解析),它会从链中的第一个对象开始查找,如果这个对象有一个名为x的属性,则会直接使用这个属性值,如果第一个对象中不存在,则继续查找链上的下一个对象,以此类推。如果没有,则最终抛出一个引用错误(ReferenceError)异常。
对象链的创建规则:当定义一个函数时,保存一个作用域链。调用这个函数时,创建一个新的对象保存它的局部变量,并添加这个对象到新保存的作用域链上。
对于嵌套函数来说,每次调用外部函数时,内部函数又会重新定义一遍。