开发生涯

js作用域

作用域

引:Engine/Scope Conversation

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的块级作用域解决。

你可能也会喜欢...

发表评论

邮箱地址不会被公开。 必填项已用*标注