let 语句声明一个块级作用域的本地变量，并且可选的将其初始化为一个值。

## 语法

    let var1 [= value1] [, var2 [= value2]] [, ..., varN [= valueN]];

### 参数

`var1`, `var2`, …, `varN`
  变量名。必须是合法的标识符。
`value1`, `value2`, …, `valueN`
  变量的初始值。可以是任意合法的表达式。

## 描述

`let`允许你声明一个作用域被限制在块级中的变量、语句或者表达式。与`var`关键字不同的是，`var`声明的变量只能是全局或者整个函数块的。

点这里可以明白我们为什么选取“let”这个名字。

### 作用域规则

`let`声明的变量只在其声明的块或子块中可用，这一点，与`var`相似。二者之间最主要的区别在于`var`声明的变量的作用域是整个封闭函数。

    function varTest() {
      var x = 1;
      if (true) {
        var x = 2;  // 同样的变量!
        console.log(x);  // 2
      }
      console.log(x);  // 2
    }

    function letTest() {
      let x = 1;
      if (true) {
        let x = 2;  // 不同的变量
        console.log(x);  // 2
      }
      console.log(x);  // 1
    }

### 简化内部函数代码

当用到内部函数的时候，`let`会让你的代码更加简单。

    var list = document.getElementById("list");

    for (let i = 1; i <= 5; i++) {
      var item = document.createElement("LI");
      item.appendChild(document.createTextNode("Item " + i));

      let j = i;
      item.onclick = function (ev) {
        console.log("Item " + j + " is clicked.");
      };
      list.appendChild(item);
    }

上面这段代码的意图是创建5个li,点击不同的li能够打印出当前li的序号。如果不用`let`，而改用`var`的话，将总是打印出 `Item 5 is Clicked`，因为 j 是函数级变量，5个内部函数都指向了同一个 j ,而 j 最后一次赋值是5。用了`let`后，j 变成块级域（也就是花括号中的块，每进入一次花括号就生成了一个块级域）,所以 5 个内部函数指向了不同的 j 。

在程序或者函数的顶层，`let`并不会像`var`一样在全局对象上创造一个属性，比如

    var x = 'global';
    let y = 'global';
    console.log(this.x); // "global"
    console.log(this.y); // undefined

### 模仿私有接口

在处理构造函数的时候，可以通过`let`声明而不是闭包来创建私有接口。

    var SomeConstructor;

    {
        let privateScope = {};

        SomeConstructor = function SomeConstructor () {
            this.someProperty = "foo";
            privateScope.hiddenProperty = "bar";
        }

        SomeConstructor.prototype.showPublic = function () {
            console.log(this.someProperty); // foo
        }

        SomeConstructor.prototype.showPrivate = function () {
            console.log(privateScope.hiddenProperty); // bar
        }

    }

    var myInstance = new SomeConstructor();

    myInstance.showPublic();
    myInstance.showPrivate();

    console.log(privateScope.hiddenProperty); // error

### let 的暂存死区与错误

在(同一个函数或)同一个作用域中用let重复定义一个变量将引起 `TypeError`.

    if (x) {
      let foo;
      let foo; // TypeError thrown.
    }

在 ECMAScript 2015 中，let 绑定不受变量提升的约束，这意味着 let 声明不会被提升到当前执行上下文的顶部。在块中的变量初始化之前，引用它将会导致 ReferenceError（而使用 var 声明变量则恰恰相反，该变量的值是 undefined ）。这个变量处于从块开始到 let 初始化处理的”暂存死区“之中。

    function do_something() {
      console.log(bar); // undefined
      console.log(foo); // ReferenceError: foo is not defined
      var bar = 1;
      let foo = 2;
    }

在 `switch` 声明中你可能会遇到这样的错误，因为一个switch只有一个作用块。

    switch (x) {
      case 0:
        let foo;
        break;

      case 1:
        let foo; // SyntaxError for redeclaration.
        break;
    }

但是，需要指出，在case语句后面可以创建新的作用域块，形成新的词法环境，这样就不会产生上述重复声明（`redeclaration`）的语法错误。

    let x = 1;

    switch(x) {
      case 0: {
        let foo;
        break;
      }
      case 1: {
        let foo;
        break;
      }
    }

`let`后跟一个函数传递的参数时将导致循环内部报错。

    function go(n){
      for (let n of n.a) { // ReferenceError: n is not defined
        console.log(n);
      }
    }

    go({a:[1,2,3]});

### 循环定义中的let作用域

循环体中是可以引用在for声明时用let定义的变量，尽管let不是出现在大括号之间.(注：该方法在 火狐 45.4.0 ,Centos7 下，报错 ReferenceError: can't access lexical declaration `i' before initialization)

    var i = 0;
    for (let i = i; i < 10; i++) {
      console.log(i);
    }

注：以上 let 声明的 i 将会变成 undefined；chrome 版本50.0.2661.102 (64-bit)；推荐以下写法：

    var i = 0;
    for (let l = i; l < 10; l++) {
    　console.log(l);
    }

#### 域作用规则

    for (let expr1; expr2; expr3) statement

在这个例子中，expr2, expr3, 和 statement 都是包含在一个隐含域块中，其中也包含了 expr1.

## 例子

### `let` 对比 `var`

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

    var a = 5;
    var b = 10;

    if (a === 5) {
      let a = 4; // The scope is inside the if-block
      var b = 1; // The scope is inside the function

      console.log(a);  // 4
      console.log(b);  // 1
    }

    console.log(a); // 5
    console.log(b); // 1

### `let` 在循环中

可以用 let 来代替 var ，在 for 定义块中使用块级变量.

    for (let i = 0; i < 10; i++) {
      console.log(i); // 0, 1, 2, 3, 4 ... 9
    }

    console.log(i); // i is not defined

## 非标准的 let 扩展

### `let块（``let` block）

let blocks 在 Gecko 44 中已经废除（ bug 1167029） 。

`let块`提供了一种在块的范围内获取变量的值，而不会影响块外面名字相同的变量的值的方法。

#### 语法

    let (var1 [= value1] [, var2 [= value2]] [, ..., varN [= valueN]]) block;

#### 描述

let 语句块为变量提供了局部作用域。它的作用是在单一代码块的词法范围内绑定零个或多个变量； 此外与普通语句块没有任何区别。需要特别注意的是， 在 `let` 语句块内使用 var 声明的变量，它的作用域与在 let 语句块之外声明没有区别；这样的变量仍然具有函数作用域。在使用 `let` 语句块时，必须使用花括号，否则会导致语法错误。

#### 例子

    var x = 5;
    var y = 0;

    let (x = x + 10, y = 12) {
      console.log(x + y); // 27
    }

    console.log(x + y); // 5

let 代码块的规则与 JavaScript 中其他类型的代码块相同。允许在块内通过 let 关键字声明局部变量。

#### 作用域规则

使用 `let` 语句块绑定的变量，其作用域是 `let` 语句块本身，与任何其内部语句块的作用域一样，除非在这些内部语句块内又定义了同名的变量。

### `let` 表达式（ let expression）

`let` expression 在 Gecko 41 已经废除（bug 1023609）。

`let`表达式 可以将变量的作用域仅作用于一条语句。

#### 语法

    let (var1 [= value1] [, var2 [= value2]] [, ..., varN [= valueN]]) expression;

#### 例子

你可以在一条语句的范围中使用 `let` 关键字来设立变量：

    var a = 5;
    let(a = 6) console.log(a); // 6
    console.log(a); // 5

#### 作用域规则

给定一个 `let` 表达式：

    let (decls) expr

这里隐式创建了一个包围 expr 的语句块。

## 规范

Specification | Status | Comment
--- | --- | ---
ECMAScript 2015 (6th Edition, ECMA-262) Let and Const Declarations | Standard | Initial definition. Does not specify let expressions or let blocks.
ECMAScript Latest Draft (ECMA-262) Let and Const Declarations | Draft |

## 浏览器兼容性

We're converting our compatibility data into a machine-readable JSON format. This compatibility table still uses the old format, because we haven't yet converted the data it contains. Find out how you can help!

- Desktop
- Mobile

Feature | Chrome | Edge | Firefox (Gecko) | Internet Explorer | Opera | Safari
--- | --- | --- | --- | --- | --- | ---
Basic support | 41.0 | (Yes) | 44 (44) | 11 | 17 | ?
Temporal dead zone | ? | (Yes) | 35 (35) | ? | ? | ?
`let` expression | 未实现 | 未实现 | 未实现 | 未实现 | 未实现 | 未实现
`let` block | 未实现 | 未实现 | 未实现 | 未实现 | 未实现 | 未实现

Feature | Android | Chrome for Android | Firefox Mobile (Gecko) | IE Mobile | Opera Mobile | Safari Mobile
--- | --- | --- | --- | --- | --- | ---
Basic support | ? | 41.0 | 44.0 (44) | ? | ? | ?
Temporal dead zone | ? | ? | 35.0 (35) | ? | ? | ?
`let` expression | 未实现 | 未实现 | 未实现 | 未实现 | 未实现 | 未实现
`let` block | 未实现 | 未实现 | 未实现 | 未实现 | 未实现 | 未实现

## Firefox-specific notes

- [1]: 只允许用在被`<script type="application/javascript;version=1.7">` 包裹的代码块中 （或者更高的 版本version）。当心，无论如何， 作为一个非标准特性， 很有可能会打破其他浏览器的支持。 XUL 脚本标签实现这些特性不需要特殊的块。 请看 bug 932517和bug 932517。
- ES6 compliance for `let` in SpIderMonkey is tracked in bug 950547 and non-standard extensions are going to be removed in the future bug 1023609.

## 相关链接

- `var`
- `const`
- ES6 In Depth: `let` and `const`
- Breaking changes in `let` and `const` in Firefox 44.
- You Don't Know JS: Scope & Closures: Chapter 3: Function vs. Block Scope