let在很多方面与var是相似的,但是可以帮助我们避免在js里常见的一些问题。const是对let的一个增强,它能阻止对一个变量再次赋值。

  var的怪异之处:也是很多的面试问题。 



function f(shouldInitialize: boolean) {
    if (shouldInitialize) {
        var x = 10;
    }

    return x;
}

f(true);  // returns '10'
f(false); // returns 'undefined'



  变量x定义在if语句里面。但是我们可以在语句外面访问它。这是因为var声明可以在包含它的函数,模块,命名空间或全局作用域内部任何位置被访问,包含它的代码块对此没有什么影响。函数参数也使用函数作用于。

  多次声明同一个变量并不会报错:



function sumMatrix(matrix: number[][]) {
    var sum = 0;
    for (var i = 0; i < matrix.length; i++) {
        var currentRow = matrix[i];
        for (var i = 0; i < currentRow.length; i++) {
            sum += currentRow[i];
        }
    }

    return sum;
}



  里层的for循环会覆盖变量i,因为所有i都引用相同的函数作用域内的变量。

  捕获变量怪异之处:下面结果是10.10.10.



for (var i = 0; i < 10; i++) {
    setTimeout(function() { console.log(i); }, 100 * i);
}



  这种结果大家都很熟悉了。这是因为“我们传给setTimeout的每一个函数表达式实际上都引用了相同作用域里的同一个i”。这种通常的解决方法是使用立即执行的函数表达式来捕获每次迭代时i的值。



for (var i = 0; i < 10; i++) {
    // capture the current state of 'i'
    // by invoking a function with its current value
    (function(i) {
        setTimeout(function() { console.log(i); }, 100 * i);
    })(i);
}



  let声明:主要的区别不在语法上,而是语义。

  块作用域:当let声明一个变量,它使用的是词法作用域或块作用域。不同于使用var声明的变量那样可以在包含它们的函数外访问,块作用域变量在包含它们的块或for循环之外是不能访问的。



function f(input: boolean) {
    let a = 100;

    if (input) {
        // Still okay to reference 'a'
        let b = a + 1;
        return b;
    }

    // Error: 'b' doesn't exist here
    return b;
}



  拥有块级作用域的变量另一个特点是,它们不能再被声明之前读写。虽然这些变量始终存在于它们的作用域里,但是直到声明它的代码之前的区域都属于暂时性死区。

  重定义及屏蔽:var声明时,它不在乎声明多少次,只会得到一个。let声明不会这么宽松了,并不是要求两个均是块级作用域的声明ts才会给出警告。在一个嵌套作用域里引用一个新名字的行为称作屏蔽。它是一把双刃剑,它可能会不小心的引入新问题,同时也会解决一些错误。之前的内嵌套循环就可以正常使用。通常来讲应该避免使用屏蔽,因为我们需要写出清晰的代码。

  当let声明出线在循环体里时拥有完全不同的行为。不仅是在循环里引用一个新的变量环境,而是针对每次迭代都会创建这样一个新作用域。这就是我们在使用立即执行的函数表达式时做的事,所以在settimeout例子里我们仅使用let声明就可以了。

  const声明是声明变量的另一种方式。它们是不可变的。

  这两种作用域相似的声明方式。有时候会问到底使用哪个,一般都是按最小特权原则。基本原则就是如果一个变量不需要对它写入,那么其它使用这些代码的人也不能够写入它们,并且要思考为什么会需要对这些变量重新赋值。