《JavaScript权威指南》第6版第三章第10节:
一个变量的作用域(scope)是程序源代码中定义这个变量的区域。全局变量拥有全局作用域,在JavaScript代码中的任何地方都是有定义的。然而在函数内声明的变量只在函数体内有定义。它们是局部变量,作用域也是局部的。函数参数也是局部变量,它们只在函数体内有定义。
在函数体内,局部变量的优先级高于同名的全局变量。如果在函数内声明的一个局部变量或函数参数中带有的变量和全局变量重名,那么全局变量就被局部变量所遮盖。
var scope = "global"; //声明一个全局变量 function checkscope(){ var scope = "local"; //声明一个同名的局部变量 return scope; //返回局部变量的值,而不是全局变量的值 } checkscope(); // ==>"local"
尽管在全局作用域编写代码时可以不写var 语句,但声明局部变量时则必须使用var。如:
scope = "global"; //声明一个全局变量,不用var function checkscope(){ scope = "local"; //糟糕!我们刚修改了全局变量的值! myscope = "local"; // 这是显式声明了一个新的全局变量 return [scope,myscope]; // 返回两个值 } checkscope(); // ==>["local","local"]:产生了副作用 scope; //"local":全局变量修改了 myscope; //"local":全局命名空间搞乱了
函数定义是可以嵌套的,由于每个函数都有它自己的作用域,因此会出现几个局部作用域嵌套的情况:
var scope = "global scope";//全局变量 function checkscope(){ var scope = "local scope";//局部变量 function nested(){ var scope = "nested scope";//嵌套作用域内的局部变量 return scope; //返回当前作用域内的值 } return scope+","+nested(); } console.log(scope+"|"+checkscope()); //==>global scope | local scope , nested scope
3.10.1 函数作用域和声明提前:
在JavaScript中,没有块级作用域的概念,取而代之的是函数作用域(funciton scope):变量在声明它们的函数体以及这个函数体嵌套的任意函数体内都是有定义的。JavaScript的函数作用域是指在函数内声明的所有变量在函数体内始终是可见的——这意味着变量在声明之前甚至已经可用。JavaScript的这个特性被非正式地称为声明提前(hoisting),即JavaScript函数里声明的所有变量(但不涉及赋值)都被提前到函数体的顶部。
var scope = "global";//全局变量 (function f(){ console.log(scope); //==>"global" }());
再对比一下:
var scope = "global";//全局变量 (function f(){ console.log(scope); //==>undefined var scope = "local"; console.log(scope); //==>"local" }());
为什么呢?——因为,局部变量scope的声明被提前了,但是赋值没有被提前,所以,以上代码相当于:
var scope = "global";//全局变量 (function f(){ var scope; //声明局部变量scope,此时没有赋值 console.log(scope); //==>undefined scope = "local"; //给局部变量scope赋值 console.log(scope); //==>"local" }());
3.10.2 作为属性的变量
当声明一个JavaScript全局变量时,实际上定义了全局对象的一个属性,当使用var声明一个变量时,创建的这个属性是不可配置的,也就是说这个变量无法通过delete运算符删除。但有一种特殊情况:如果没有使用严格模式并给一个未声明的变量赋值的话,JavaScript会自动创建一个全局变量,以这种方式创建的变量是全局对象的可配置值属性并可以删除它们:
var truevar = 1; //声明一个不可删除的全局变量 fakevar = 2;//创建全局对象的一个可删除的属性 this.fakevar2 = 3;//同上 console.log( delete truevar);//==>false console.log( delete fakevar);//==>true console.log( delete fakevar2);//==>true