到现在已经学了将近一个月的JS了,对一个不折不扣的新手来说,感觉弄清楚作用域对于学习JS极其重要,所以我的第一篇blog主题就是JS作用域详解。如果文中有错误的地方,欢迎指正!
作用域链与执行环境
什么是执行环境:
执行环境定义了变量或函数有权访问的其他数据,决定了它们各自的行为。每个函数都有自己的执行环境。
什么是作用域链:
作用域链的用途是保证对执行环境有权访问的所有变量和函数的有序访问。
先来看一个简单例子:
var name="lucy";
function changePerson(){
var gender=null;
if(name==="lucy"){
name="david";
gender="male";
}else{
name="lucy";
gender="lady";
}
}
changePerson();
alert("name is "+name); //name is david
alert("gender is "+ gender); //Uncaught ReferenceError: gender is not defined
- 这里changeName()的作用域包含两个对象:arguments对象和全局环境对象。changeName()之所以可以在函数内部访问变量color和gender,是因为可以在这个作用域链中找到它。
- 以上代码共涉及两个执行环境:全局环境和changePerson()的局部环境。全局环境中有一个变量name和两个函数:changePerson(),alert().changePerson()的局部环境中有一个变量gender。因为gender 被定义在changePerson()的局部环境中,全局环境无法访问到它,所以报错,而name可以被访问到是因为它本身就存在于全局环境中。
这里还要指出一个可能犯错的地方:
var name="lucy";
function changePerson(){
gender=null;
if(name==="lucy"){
name="david";
gender="male";
}else{
name="lucy";
gender="lady";
}
}
changePerson();
alert("name is "+name); //name is david
alert("gender is "+ gender); //gender is male
也行你可能会问,为什么这里gender就可以被访问到了呢?其实这两块代码非常相似,唯一的区别就是这次我特意漏写了gender之前的var,因为没有使用关键词var,所以gender被定义成了全局变量,那么自然也就能被alert()访问到了。当然故意这么做是不被提倡的,建议如果确实要定义一个全局变量的话,显示地定义在window对象上,因为在web浏览器中,全局执行环境被认为是window对象。
综上,我们可以得出:内部环境可以通过作用域链访问所有的外部环境,但外部环境不能访问内部环境中的任何变量和函数
JavaScript的块级作用域
什么是块级作用域
任何一对花括号({})中的语句集都属于一个块,在这之中定义的所有变量在代码块外都是不可见的,我们称之为块级作用域。
比如我们用Java写一个最简单的for循环:
for(int i=0;i<5;i++){
//do something
}
System.out.println(i); //编译器无法找到i,因为i被定义在一个不可见的代码块内
JavaScript中没有块级作用域
当上述的例子用JavaScript来写时:
for(var i=0;i<5;i++){
//do something
}
alert(i); //5
可以看到答案是5,也就是成功的得到了i的值,因为JS没有块级作用域,所以其实上面代码的效果等同于:
var i=0;
for(i=0;i<5;i++){
//do something
}
alert(i); //5 (毫无疑问,结果是相同的)
块级作用域的作用
从我们上述的例子来看,如果没有块级作用域,对于刚接触JS的新手来说,一不小心就可能会声明很多全局变量,这时候便会发生变量名冲突等问题。而在Java和C中我们并不用担心这一点。
参考
- IcyFox
- Javascript高级程序设计