作用域
引:Engine/Scope Conversation
from You Don’t Know JS: Scope & Closures
这一章节,生动详细的向我们描述了js解析引擎是如何运行的,这对理解我们的作用域非常有帮助,有兴趣的可以瞅一眼。下面通过几个简单的题来巩固一下。
1.下面代码alert
显示的内容是什么?
function a() {
var foo = 1;
b();
}
function b() {
alert(foo);
}
function c() {
var foo = 1;
d();
function d() {
alert(foo);
}
}
a();
c();
这道题主要考察的是js中变量的作用域的问题。运行a()
,得到的结果是foo is not defined
,且程序停止,不会再执行c()
,如果把a()
注释掉的话,会alert(1)
。
function a(){}
中声明的变量foo
是局部变量,无法被外界访问,所以function b(){}
会报错。
而function d(){}
中的foo
虽然也是局部变量,但是foo
的作用域恰好包括了function d(){}
,所以这个foo
可以找到。
2.下面代码console
显示的内容是什么?
var scope="global";
function t(){
console.log(scope);
var scope = "local";
console.log(scope);
}
t();
有些童鞋想当然的认为,因为代码还没有执行到var scope = "local"
,所以第一个显示的内容是global
,第二个是local
。
然而事实却不是这样的,第一个显示的内容是undefined
。如果按照js解析引擎去解读这段代码,应该可以这样去理解:
var scope; //只是知道有这个变量,还没有进行赋值
function t() {} //函数声明提前
t(); //执行这个方法
function t() { //开始观察方法的内部
var scope; //这个是局部变量,变量声明提前
console.log(scope); //没有赋值,所以为undefined
scope = "local"; //对局部变量进行赋值
console.log(scope; //显示为 local
}
3.es5跟es6的作用域区别
在es5中,是没有块级作用域这个概念的,只有函数作用域,下面的这个例子就可以说明:
var name="global";
if(true){
var name="local";
console.log(name)
}
console.log(name);
这里的两个console
显示的内容都是local
,因为如果有块级作用域的话,第二个console
出来的内容应该是global
,但是name
这个变量被if
中的name
给覆盖了。
es6中,多了let
这个声明变量的关键字,使用let
声明变量的作用域是块级作用域。还是上面es5中的代码,我们将if
中的var
变为let
看一下:
var name="global";
if(true){
let name="local";
console.log(name)
}
console.log(name);
这时,console
出来的内容就是
local
global
这就证实了,使用let
声明变量的作用域是块级作用域。
4.为下面html中每一个li添加点击事件,点击alert
出自己为ul中的第几个。
<ul>
<li>内容</li>
<li>内容</li>
<li>内容</li>
<li>内容</li>
<li>内容</li>
</ul>
下面代码中是否有问题?如果有应该如何改善?
var liList = document.getElementsByTagName('li');
for(var i = 0, li; li = liList[i]; i++) {
li.addEventListener('click', function() {
alert(i);
}, false);
}
这道题有两个问题,第一个是for循环的写法上存在问题,没有说明i的最大值是多少;第二个问题是根据js中变量的作用域链来看,i
在这个匿名函数中没有声明,所以会去外面找,但是这个i因为没有最大值,一直++,所以也不会知道i是多少。
应该使用如下方法去写:
方法一:
var liList = document.getElementsByTagName('li');
for(var i = 0, len = liList.length; i < len; i++) {
(function (i) {
liList[i].addEventListener('click', function () {
alert(i);
});
})(i);
}
这种方法使用闭包的方式,将i传入。不会出现每次调用因为匿名函数中找不到i,去外部找,i已经加到最大值的情况。
方法二:
for(let i = 0, len = liList.length; i < len; i++) {
liList[i].addEventListener('click', function () {
alert(i);
})
}
使用es6的块级作用域解决。