变量作用域
在JavaScript中,用var申明的变量实际上是有作用域的。
如果一个变量在函数体内部申明,则该变量的作用域为整个函数体,在函数体外不可引用该变量。
如果两个不同的函数各自申明了同一变量,那么该变量只在各自的函数体内起作用。换句话说,不同函数内部的同名变量互相独立,互不影响。
由于JavaScript的函数可以嵌套,此时,内部函数可以访问外部函数定义的变量,反过来则不行:
function foo() { var x = 1; function bar() { var y = x + 1 ;//bar可以访问foo的变量x } var z = y + 1;//ReferenceError!foo不可以访问bar的变量y! }
如果内部函数和外部函数的变量名重名怎么办?
function foo() { var x = 1; function bar() { var x = 'A'; alert('x in bar() =' + x);//'A' } alert('x in foo() =' + x );//1 bar(); }
JavaScript的函数在查找变量时从自身函数定义开始,从“内”向“外”查找。如果内部函数定义了与外部函数重名的变量,则内部函数的变量将“屏蔽”外部函数的变量。
变量提升
JavaScript的函数定义有个特点,它会先扫描整个函数体的语句,把所有申明的变量“提升”到函数顶部:
function foo() { var x = "Hello," + y ; alert(x); var y = "Bob"; } foo();//Hello,undefined
语句 var x = "Hello," + y ;并不会报错,原因是变量y在稍后申明了。但是alert显示的是Hello,undefined,说明变量y的值是undefined,这正是因为JavaScript引擎自动提升了变量y的声明,但是不会提升变量y的赋值语句。
对于上述的foo()函数,JavaScript引擎看到的代码相当于:
function foo() { var y;//提升变量y的申明 var x = 'Hello,' + y ; alert(x); y='Bob'; }
tips:所以在JavaScript函数内部要首先申明所有变量。最常见的做法是用一个var申明函数内部用到的所有变量:
function foo() { var x=1,//x初始化为1 y=x+1,//y初始化为2 z,i;//z和i为undefined //其他语句 for(i = 0; i<100; i++){ ... } }
全局作用域
不在任何函数内定义的变量就具有全局作用域。JavaScript默认有一个全局对象window,全局作用域的变量实际上被绑定到window的一个属性:
var course = 'Learn JavaScript'; alert(course);//'Learn JavaScript' alert(window.course);//'Learn JavaScript'
因此,直接访问全局变量course和访问window.course是完全一样的。
由于函数定义有两种方式,以变量方式var foo = function () {}定义的函数实际上也是一个全局变量。因此,顶层函数的定义也被视为一个全局变量
名字空间
全局变量会绑定到window上,不同的JavaScript文件如果使用了相同的全局变量,或者定义了相同名字的顶层函数,都会造成命名冲突,并且很难被发现。
减少冲突的一个方法是把自己的所有变量和函数全部绑定到一个全局变量中。
//唯一的全局变量MYAPP: var MYAPP = {}; //其他变量: MYAPP.name = 'myapp'; MYAPP.version = 1.0; //其他函数 MYAPP.foo = function () { return 'foo'; };
把自己的代码全部放入唯一的命名空间MYAPP中,会大大减少全局变量冲突的可能。
局部作用域
由于JavaScript的变量作用域实际上是函数内部,我们在for循环等语句块中无法定义具有局部作用域的变量:
'use strict': function foo() { for( var i = 0 ; i < 100; i++){ // } i +=100;//仍然可以引用变量i }
为了解决块级作用域,ES6引入了新的关键字let,用let替代var可以申明一个块级作用域的变量:
'use strict': function foo() { var sum = 0 ; for (let i = 0 ; i < 100 ; i++){ sum +=i; } i += 1;//SyntaxError }
常量:
由于var和let申明的是变量,在ES6之前是不行的,我们通常用全部大写的变量来表示“这是一个常量,不要修改它的值”:
var PI = 3.14;
ES6标准引入了新的关键字const来定义常量,const与let都具有块级作用域:
'use strict': const PI = 3.14; PI = 3;//某些浏览器不报错,但是无效果! PI;//3.14