JavaScript中的作用域问题

148 阅读2分钟

下面的代码会出现典型的作用域问题:

for(var i=0;i<10;i++){

    var btn=document.createElement('button');

    btn.innerHTML=i;btn.onclick=function(){

       alert(i);  //所有的按钮点击后都弹出10
    }

document.body.appendChild(btn);
}

为什么说这个是作用域问题?

首先在这个循环中,i是使用var定义在for循环中的,var定义的是全局变量。循环进行时,会依次给每个按钮绑定事件onclcik,到这里都是没有问题的,问题出在函数存在函数作用域,并且函数中的i并没有被定义。

在这段代码中,全局作用域只有一个i。也就是说,循环的过程中,i的值也在不断改变。因为使用var声明的变量,是会被后面的同名的变量改变的。当我们点击按钮时,函数执行,此时函数会去查找i的值,因为函数作用域中没有i,就会继续顺着作用域链查找,查找到全局变量i,也就是全局变量i,此时i的值已经被循环改变了,造成的结果就是,我们不管点击哪个按钮,i都是10。

ES5中闭包解决作用域问题

for(var i=0;i<10;i++){

    (function(i){

        var btn=document.createElement('button');

        btn.innerHTML=i;

        btn.onclick=function(){

            alert(i);  //点击后都弹出对应的数字

        }

    document.body.appendChild(btn);

    })(i)

}

ES5中使用的匿名执行函数,其实就是将循环中的i值作为参数传递进去,每循环一次,就传递当前的变量i进入函数,好让函数内部的作用域存在局部变量i,也就避免了向上去查找全局变量i。

ES6中let解决作用域问题

for(let i=0;i<10;i++){

    let btn=document.createElement('button');

    btn.innerHTML=i;

    btn.onclick=function(){

    alert(i);  //点击后都弹出对应的数字

    }

document.body.appendChild(btn);

}

ES6的let解决方案,其实和ES5的匿名函数有着异曲同工之处,区别在于,let已经自动帮我们生成了独立的块级作用域,每循环一次就生成一个新的块级作用域,并且作用域中的i每次都是一个新的值,也不会给改变。