var引发的问题与ES6:let

Posted by luoway on November 11, 2015

问题

对比以下3种代码,发现代码2有差异。

1.直接赋值

function foo() {
    var arr = []; 
    for (var i=0; i <10; i++) {
        arr[i] = i;  
    }   
    return arr; 
}

var fn = foo();

for (var i = 0; i < fn.length; i++) {
    console.log( fn[i] );
}//0 ~ 9

2.函数返回

function foo() {
    var arr = []; 
    for (var i=0; i <10; i++) {
        arr[i] = function() {
            return i;
        };  
    }   
    return arr; 
}

var fn = foo();

for (var i = 0; i < fn.length; i++) {
    console.log( fn[i]() );
}//10 个 10

3.给函数传参数

function foo() {
    var arr = []; 
    for (var i=0; i <10; i++) {
        arr[i] = function(a) {
            return a;
        };  
    }   
    return arr; 
}

var fn = foo();

for (var i = 0; i < fn.length; i++) {
    console.log( fn[i](i) );
}//0 ~ 9

描述

1.直接赋值

foo()中定义一个数组,给数组中的元素赋值,返回该数组。 调用foo()函数,将返回数组赋值给fn,遍历输出fn。

2.函数返回

foo()中定义一个数组,给数组中的元素赋匿名函数,返回该函数数组。

arr[i] = function (){return i;};

等价于(事实上,前者为函数表达式,后者为声明函数,不完全等价。执行顺序不同。)

function arr[i](){return i;}
arr[i]();

可以发现,i 不是作为参数传入名为 arr[i] 的函数的,
调用 foo() 函数,将返回函数数组赋值给 fn 后,遍历调用 fn 数组中的函数。

需要注意的是,此时外函数foo()中的for循环已经结束了,i = 10,则内函数 arr[i]() 在作用域链上访问到的 i = 10
因此,调用 fn 数组中的每个函数输出结果都为10。

3.给函数传参数

arr[i] = function (a){return a;};

等价于

function arr[i](a){return a;}
arr[i](i);

此时,调用 fn 数组中的每个函数,传入参数i使用的都是第二个的for循环中的i值。

let定义

MDN-JavaScript:let

let 关键字申明了一个块级域的本地变量,在申明变量的时候可同时赋值。

作用域规则:用 let 定义的变量的作用域是定义它们的块内,以及包含在这个块中的子块 ,这一点有点像var,只是 var 定义的变量的作用域是定义它们的函数内

let的作用域是块,而var的作用域是函数

可见 块级作用域函数级作用域 的不同,是 letvar 的区别。

function foo() {
    var arr = []; 
    for (let i=0; i <10; i++) {
        arr[i] = function() {
            return i;
        };  
    }   
    return arr; 
}

var fn = foo();

for (var i = 0; i < fn.length; i++) {
    console.log( fn[i]() );
}//0 ~ 9

只需修改代码2的foo()中for循环的varlet,即可完成for循环块中所有i值相同的设定。

使用let可以直观地辨别变量的作用域,简化阅读代码和编写代码的理解难度。