首页 文章

使用“let”和“var”在JavaScript中声明变量有什么区别?

提问于
浏览
3522

ECMAScript 6引入了the let statement . 我've heard it described as a 120236 variable, but I'仍然不太确定它与 var 关键字的行为有何不同 .

有什么区别?什么时候 let 应该在 var 上使用?

27 回答

  • 2

    以下是两者之间差异的示例(支持刚启动的chrome):
    enter image description here

    正如您所看到的, var j 变量仍然具有for循环范围之外的值(块范围),但 let i 变量在外部未定义for循环范围 .

    "use strict";
    console.log("var:");
    for (var j = 0; j < 2; j++) {
      console.log(j);
    }
    
    console.log(j);
    
    console.log("let:");
    for (let i = 0; i < 2; i++) {
      console.log(i);
    }
    
    console.log(i);
    
  • 4895

    ECMAScript 6添加了一个关键字来声明除"let"之外的"const"以外的变量 .

    在"var"上引入"let"和"const"的主要目标是使用块范围而不是传统的词法范围 . This article explains very briefly difference between "var" and "let" and it also covers the discussion on "const" .

  • 20

    功能VS块范围:

    varlet 之间的主要区别在于 var 声明的变量是 function scoped . 而使用 let 声明的函数是 block scoped . 例如:

    function testVar () {
      if(true) {
        var foo = 'foo';
      }
    
      console.log(foo);
    }
    
    testVar();  
    // logs 'foo'
    
    
    function testLet () {
      if(true) {
        let bar = 'bar';
      }
    
      console.log(bar);
    }
    
    testLet(); 
    // reference error
    // bar is scoped to the block of the if statement
    

    variables with var:

    当调用第一个函数 testVar 时,仍然可以在 if 语句之外访问使用 var 声明的变量foo . 此变量 footestVar function 范围内可用 everywhere .

    variables with let:

    当第二个函数 testLet 被调用时,使用 let 声明的变量bar只能在 if 语句中访问 . 因为用 let 声明的变量是 block scoped (其中块是大括号之间的代码,例如 if{}for{}function{} ) .

    让变量不被提升:

    varlet 之间的另一个区别是使用 let don't get hoisted 声明的变量 . 一个示例是说明此行为的最佳方式:

    let don't 的变量被提升:

    console.log(letVar);
    
    let letVar = 10;
    // referenceError, the variable doesn't get hoisted
    

    var do 变量被挂起:

    console.log(varVar);
    
    var varVar = 10;
    // logs undefined, the variable gets hoisted
    

    Global let没有附加到窗口:

    在全局范围内使用 let 声明的变量(不是函数中的代码)不会作为属性添加到全局 window 对象上 . 例如(此代码在全局范围内):

    var bar = 5;
    let foo  = 10;
    
    console.log(bar); // logs 5
    console.log(foo); // logs 10
    
    console.log(window.bar);  
    // logs 5, variable added to window object
    
    console.log(window.foo);
    // logs undefined, variable not added to window object
    

    何时应该使用var?

    尽可能使用 let 而不是 var ,因为它的范围更加具体 . 这减少了在处理大量变量时可能发生的潜在命名冲突 . 当您希望显式地将全局变量放在 window 对象上时,可以使用 var (如果确实需要,请务必仔细考虑) .

  • 5

    let和var有什么区别?

    • 使用 var 语句定义的变量在the function中是已知的,它是从函数的开头定义的 . (*)

    • 使用 let 语句定义的变量仅在the block中已知,从定义之后定义 . (**)

    要了解其中的差异,请考虑以下代码:

    // i IS NOT known here
    // j IS NOT known here
    // k IS known here, but undefined
    // l IS NOT known here
    
    function loop(arr) {
        // i IS known here, but undefined
        // j IS NOT known here
        // k IS known here, but has a value only the second time loop is called
        // l IS NOT known here
    
        for( var i = 0; i < arr.length; i++ ) {
            // i IS known here, and has a value
            // j IS NOT known here
            // k IS known here, but has a value only the second time loop is called
            // l IS NOT known here
        };
    
        // i IS known here, and has a value
        // j IS NOT known here
        // k IS known here, but has a value only the second time loop is called
        // l IS NOT known here
    
        for( let j = 0; j < arr.length; j++ ) {
            // i IS known here, and has a value
            // j IS known here, and has a value
            // k IS known here, but has a value only the second time loop is called
            // l IS NOT known here
        };
    
        // i IS known here, and has a value
        // j IS NOT known here
        // k IS known here, but has a value only the second time loop is called
        // l IS NOT known here
    }
    
    loop([1,2,3,4]);
    
    for( var k = 0; k < arr.length; k++ ) {
        // i IS NOT known here
        // j IS NOT known here
        // k IS known here, and has a value
        // l IS NOT known here
    };
    
    for( let l = 0; l < arr.length; l++ ) {
        // i IS NOT known here
        // j IS NOT known here
        // k IS known here, and has a value
        // l IS known here, and has a value
    };
    
    loop([1,2,3,4]);
    
    // i IS NOT known here
    // j IS NOT known here
    // k IS known here, and has a value
    // l IS NOT known here
    

    在这里,我们可以看到我们的变量 j 仅在第一个for循环中已知,但不在之前和之后 . 然而,我们的变量 i 在整个函数中是已知的 .

    另外,请考虑块范围变量在声明之前是未知的,因为它们没有被提升 . 您也不允许在同一个块中重新声明相同的块范围变量 . 这使得块范围变量比全局或功能范围变量更不容易出错,这些变量被提升并且在多个声明的情况下不会产生任何错误 .


    今天使用它是否安全?

    有些人会争辩说,将来我们只会使用let语句,而var语句将会过时 . JavaScript大师Kyle Simpson写了a very elaborate article on why that's not the case .

    然而,今天绝对不是这样 . 事实上,我们实际上需要问自己,使用 let 语句是否安全 . 这个问题的答案取决于您的环境:

    • 如果您正在编写服务器端JavaScript代码(Node.js),则可以安全地使用 let 语句 .

    • 如果您正在编写客户端JavaScript代码并使用转换器(如Traceur),则可以安全地使用 let 语句,但是您的代码在性能方面可能不是最优的 .

    • 如果您正在编写客户端JavaScript代码而不使用转换器,则需要考虑浏览器支持 .

    今天,2018年6月8日,仍然有一些浏览器不支持 let


    如何跟踪浏览器支持

    有关在阅读此答案时哪些浏览器支持 let 语句的最新概述,请参阅this Can I Use page .


    (*)全局和功能范围的变量可以在声明之前初始化和使用,因为JavaScript变量是hoisted . 这意味着声明始终位于范围的顶部 .

    (**)未提升块范围变量

  • 476

    一些hacks let

    1 .

    let statistics = [16, 170, 10];
        let [age, height, grade] = statistics;
    
        console.log(height)
    

    2 .

    let x = 120,
        y = 12;
        [x, y] = [y, x];
        console.log(`x: ${x} y: ${y}`);
    

    3 .

    let node = {
                       type: "Identifier",
                       name: "foo"
                   };
    
        let { type, name, value } = node;
    
        console.log(type);      // "Identifier"
        console.log(name);      // "foo"
        console.log(value);     // undefined
    
        let node = {
            type: "Identifier"
        };
    
        let { type: localType, name: localName = "bar" } = node;
    
        console.log(localType);     // "Identifier"
        console.log(localName);     // "bar"
    

    Getter和setter with let:

    let jar = {
        numberOfCookies: 10,
        get cookies() {
            return this.numberOfCookies;
        },
        set cookies(value) {
            this.numberOfCookies = value;
        }
    };
    
    console.log(jar.cookies)
    jar.cookies = 7;
    
    console.log(jar.cookies)
    
  • 41

    这是一个带有一些例子的explanation of the let keyword .

    让我的工作非常像var . 主要区别在于范围var变量是整个封闭函数

    维基百科上的This table显示哪些浏览器支持Javascript 1.7 .

    请注意,只有Mozilla和Chrome浏览器支持它 . IE,Safari和其他人可能没有 .

  • 131
    • 变量未挂起

    让我们不要提升它们出现的整个范围 . 相比之下,var可以如下所示提升 .

    {
       console.log(cc); // undefined. Caused by hoisting
       var cc = 23;
    }
    
    {
       console.log(bb); // ReferenceError: bb is not defined
       let bb = 23;
    }
    

    实际上,Per @Bergi,Both var and let are hoisted .

    • Garbage Collection

    let 的块范围与闭包和垃圾收集有关,以回收内存 . 考虑,

    function process(data) {
        //...
    }
    
    var hugeData = { .. };
    
    process(hugeData);
    
    var btn = document.getElementById("mybutton");
    btn.addEventListener( "click", function click(evt){
        //....
    });
    

    click 处理程序回调根本不需要 hugeData 变量 . 从理论上讲,在 process(..) 运行之后,巨大的数据结构 hugeData 可能会被垃圾收集 . 但是,有些JS引擎仍然需要保留这个庞大的结构,因为 click 函数在整个范围内都有一个闭包 .

    但是,块范围可以使这个庞大的数据结构被垃圾收集 .

    function process(data) {
        //...
    }
    
    { // anything declared inside this block can be garbage collected
        let hugeData = { .. };
        process(hugeData);
    }
    
    var btn = document.getElementById("mybutton");
    btn.addEventListener( "click", function click(evt){
        //....
    });
    
    • let loops

    循环中的 let 可以 re-binds it 到循环的每次迭代,确保从前一循环迭代结束时重新赋值 . 考虑,

    // print '5' 5 times
    for (var i = 0; i < 5; ++i) {
        setTimeout(function () {
            console.log(i);
        }, 1000);  
    }
    

    但是,将 var 替换为 let

    // print 1, 2, 3, 4, 5. now
    for (let i = 0; i < 5; ++i) {
        setTimeout(function () {
            console.log(i);
        }, 1000);  
    }
    

    因为 let 创建了一个新的词法环境,其中包含a)初始化表达式b)每次迭代(主要用于评估增量表达式)的名称,更多细节是here .

  • 6

    可能以下两个函数显示了不同之处:

    function varTest() {
        var x = 31;
        if (true) {
            var x = 71;  // Same variable!
            console.log(x);  // 71
        }
        console.log(x);  // 71
    }
    
    function letTest() {
        let x = 31;
        if (true) {
            let x = 71;  // Different variable
            console.log(x);  // 71
        }
        console.log(x);  // 31
    }
    
  • 134

    When Using let

    let 关键字将变量声明附加到它所包含的任何块(通常是 { .. } 对)的范围内 . 换句话说, let 隐式劫持任何块的变量声明范围 .

    let 变量无法在 window 对象中访问,因为它们无法全局访问 .

    function a(){
        { // this is the Max Scope for let variable
            let x = 12;
        }
        console.log(x);
    }
    a(); // Uncaught ReferenceError: x is not defined
    

    When Using var

    var 和ES5中的变量在函数中具有范围,这意味着变量在函数内有效,而不在函数本身之外 .

    var 变量可以在 window 对象中访问,因为它们无法全局访问 .

    function a(){ // this is the Max Scope for var variable
        { 
            var x = 12;
        }
        console.log(x);
    }
    a(); // 12
    

    If you want to know more continue reading below

    关于范围的最着名的面试问题之一也足以满足 letvar 的确切用法,如下所示;

    When using let

    for (let i = 0; i < 10 ; i++) {
        setTimeout(
            function a() {
                console.log(i); //print 0 to 9, that is literally AWW!!!
            }, 
            100 * i);
    }
    

    这是因为当使用 let 时,对于每个循环迭代,变量都是作用域的并且有自己的副本 .

    When using var

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

    这是因为当使用 var 时,对于每个循环迭代,变量都是作用域并具有共享副本 .

  • 12

    let 很有意思,因为它允许我们做这样的事情:

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

    这导致计数[0,7] .

    (() => {
        var count = 0;
    
        for (var i = 0; i < 2; ++i) {
            for (var i = 0; i < 2; ++i) {
                for (var i = 0; i < 2; ++i) {
                    console.log(count++);
                }
            }
        }
    })();
    

    仅计数[0,1] .

  • 3

    正如刚才提到的:

    区别在于范围界定 . var的范围限定为最近的功能块,并且将范围限定为最近的封闭块,该封闭块可以小于功能块 . 如果在任何区块之外,两者都是全局的 . 让我们看一个例子:

    Example1:

    在我的两个例子中,我有一个函数 myfunc . myfunc 包含一个变量 myvar 等于10.在我的第一个例子中,我检查 myvar 是否等于10( myvar==10 ) . 如果是,我使用 var 关键字声明变量 myvar (现在我有两个myvar变量)并为其赋值一个新值(20) . 在下一行中,我在我的控制台上打印它的值 . 在条件块之后,我再次在我的控制台上打印 myvar 的值 . 如果查看 myfunc 的输出, myvar 的值等于20 .

    Example2: 在我的第二个例子中,我没有在条件块中使用 var 关键字,而是使用 let 关键字声明 myvar . 现在,当我调用 myfunc 时,我得到两个不同的输出: myvar=20myvar=10 .

    所以区别很简单,即范围 .

  • 101

    块范围

    使用 let 关键字声明的变量是块作用域的,这意味着它们仅在声明它们的block中可用 .

    在顶层(功能之外)

    在顶层,使用 let 声明的变量不会在全局对象上创建属性 .

    var globalVariable = 42;
    let blockScopedVariable = 43;
    
    console.log(globalVariable); // 42
    console.log(blockScopedVariable); // 43
    
    console.log(this.globalVariable); // 42
    console.log(this.blockScopedVariable); // undefined
    

    在函数内部

    在函数内部(但在块之外), letvar 具有相同的范围 .

    (() => {
      var functionScopedVariable = 42;
      let blockScopedVariable = 43;
    
      console.log(functionScopedVariable); // 42
      console.log(blockScopedVariable); // 43
    })();
    
    console.log(functionScopedVariable); // ReferenceError: functionScopedVariable is not defined
    console.log(blockScopedVariable); // ReferenceError: blockScopedVariable is not defined
    

    在一个街区内

    在块内部使用 let 声明的变量无法在该块外部访问 .

    {
      var globalVariable = 42;
      let blockScopedVariable = 43;
      console.log(globalVariable); // 42
      console.log(blockScopedVariable); // 43
    }
    
    console.log(globalVariable); // 42
    console.log(blockScopedVariable); // ReferenceError: blockScopedVariable is not defined
    

    在循环中

    在循环中使用 let 声明的变量只能在该循环内引用 .

    for (var i = 0; i < 3; i++) {
      var j = i * 2;
    }
    console.log(i); // 3
    console.log(j); // 4
    
    for (let k = 0; k < 3; k++) {
      let l = k * 2;
    }
    console.log(typeof k); // undefined
    console.log(typeof l); // undefined
    // Trying to do console.log(k) or console.log(l) here would throw a ReferenceError.
    

    带闭包的循环

    如果在循环中使用 let 而不是 var ,则每次迭代都会得到一个新变量 . 这意味着您可以安全地在循环内使用闭包 .

    // Logs 3 thrice, not what we meant.
    for (var i = 0; i < 3; i++) {
      setTimeout(() => console.log(i), 0);
    }
    
    // Logs 0, 1 and 2, as expected.
    for (let j = 0; j < 3; j++) {
      setTimeout(() => console.log(j), 0);
    }
    

    时间死区

    由于the temporal dead zone,使用 let 声明的变量在声明之前无法访问 . 尝试这样做会引发错误 .

    console.log(noTDZ); // undefined
    var noTDZ = 43;
    console.log(hasTDZ); // ReferenceError: hasTDZ is not defined
    let hasTDZ = 42;
    

    没有重新申报

    您不能使用 let 多次声明相同的变量 . 您也无法使用声明变量 let 与使用 var 声明的另一个变量具有相同的标识符 .

    var a;
    var a; // Works fine.
    
    let b;
    let b; // SyntaxError: Identifier 'b' has already been declared
    
    var c;
    let c; // SyntaxError: Identifier 'c' has already been declared
    

    const

    const 非常类似于 let -it的块范围并且具有TDZ . 然而,有两件事是不同的 .

    无需重新分配

    使用 const 声明的变量无法重新分配 .

    const a = 42;
    a = 43; // TypeError: Assignment to constant variable.
    

    请注意,这并不意味着该值是不可变的 . 它的属性仍然可以改变 .

    const obj = {};
    obj.a = 42;
    console.log(obj.a); // 42
    

    如果要拥有不可变对象,则应使用Object.freeze() .

    初始化程序是必需的

    使用 const 声明变量时,始终必须指定一个值 .

    const a; // SyntaxError: Missing initializer in const declaration
    
  • 1

    var 是全局范围(可提升)变量 .

    letconst 是块范围 .

    test.js

    {
        let l = 'let';
        const c = 'const';
        var v = 'var';
        v2 = 'var 2';
    }
    
    console.log(v, this.v);
    console.log(v2, this.v2);
    console.log(l); // ReferenceError: l is not defined
    console.log(c); // ReferenceError: c is not defined
    
  • 6

    let 也可用于避免闭包问题 . 它绑定了新的 Value ,而不是保留旧的参考,如下面的例子所示 .

    DEMO

    for(var i = 1; i < 6; i++) {
      document.getElementById('my-element' + i)
        .addEventListener('click', function() { alert(i) })
    }
    

    上面的代码演示了一个典型的JavaScript闭包问题对 i 变量的引用存储在单击处理程序闭包中,而不是 i 的实际值 .

    每个单击处理程序都将引用同一个对象,因为只有一个计数器对象可以容纳6,因此每次单击时会得到6个 .

    一般的解决方法是将它包装在一个匿名函数中并传递 i 作为参数 . 现在也可以使用 let 而不是 var 来避免此类问题,如下面的代码所示 .

    DEMO(在Chrome和Firefox 50中测试过)

    'use strict';
    
    for(let i = 1; i < 6; i++) {
      document.getElementById('my-element' + i)
        .addEventListener('click', function() { alert(i) })
    }
    
  • 1

    本文明确定义了var,let和const之间的区别

    const是一个不会重新分配标识符的信号 . let,是一个可以重新分配变量的信号,例如循环中的计数器,或算法中的值交换 . 它还表示变量将仅在其定义的块中使用,而这并不总是包含整个函数 . var现在是在JavaScript中定义变量时可用的最弱信号 . 变量可以重新分配,也可以不重新分配,变量可以用于或不用于整个函数,或仅用于块或循环的目的 .

    https://medium.com/javascript-scene/javascript-es6-var-let-or-const-ba58b8dcde75#.esmkpbg9b

  • 17

    接受的答案缺少一点:

    {
      let a = 123;
    };
    
    console.log(a); // ReferenceError: a is not defined
    
  • 48

    MDN中查看此链接

    let x = 1;
    
    if (x === 1) {
    let x = 2;
    
    console.log(x);
    // expected output: 2
    }
    
    console.log(x);
    // expected output: 1
    
  • 41

    区别在于范围界定 . var 的范围限定为最近的功能块, let 的范围限定为最近的封闭块,该封闭块可以小于功能块 . 如果在任何区域之外,两者都是全球

    此外,使用 let 声明的变量在它们的封闭块中声明之前是不可访问的 . 如演示中所示,这将引发ReferenceError异常 .

    Demo:

    var html = '';
    
    write('#### global ####\n');
    write('globalVar: ' + globalVar); //undefined, but visible
    
    try {
      write('globalLet: ' + globalLet); //undefined, *not* visible
    } catch (exception) {
      write('globalLet: exception');
    }
    
    write('\nset variables');
    
    var globalVar = 'globalVar';
    let globalLet = 'globalLet';
    
    write('\nglobalVar: ' + globalVar);
    write('globalLet: ' + globalLet);
    
    function functionScoped() {
      write('\n#### function ####');
      write('\nfunctionVar: ' + functionVar); //undefined, but visible
    
      try {
        write('functionLet: ' + functionLet); //undefined, *not* visible
      } catch (exception) {
        write('functionLet: exception');
      }
    
      write('\nset variables');
    
      var functionVar = 'functionVar';
      let functionLet = 'functionLet';
    
      write('\nfunctionVar: ' + functionVar);
      write('functionLet: ' + functionLet);
    }
    
    function blockScoped() {
      write('\n#### block ####');
      write('\nblockVar: ' + blockVar); //undefined, but visible
    
      try {
        write('blockLet: ' + blockLet); //undefined, *not* visible
      } catch (exception) {
        write('blockLet: exception');
      }
    
      for (var blockVar = 'blockVar', blockIndex = 0; blockIndex < 1; blockIndex++) {
        write('\nblockVar: ' + blockVar); // visible here and whole function
      };
    
      for (let blockLet = 'blockLet', letIndex = 0; letIndex < 1; letIndex++) {
        write('blockLet: ' + blockLet); // visible only here
      };
    
      write('\nblockVar: ' + blockVar);
    
      try {
        write('blockLet: ' + blockLet); //undefined, *not* visible
      } catch (exception) {
        write('blockLet: exception');
      }
    }
    
    function write(line) {
      html += (line ? line : '') + '
    '; } functionScoped(); blockScoped(); document.getElementById('results').innerHTML = html;
    <pre id="results"></pre>
    

    全球:

    在功能块之外使用它们非常相似 .

    let me = 'go';  // globally scoped
    var i = 'able'; // globally scoped
    

    但是,使用 let 定义的全局变量不会作为属性添加到全局 window 对象上,就像 var 定义的那样 .

    console.log(window.me); // undefined
    console.log(window.i); // 'able'
    

    功能:

    在功能块中使用时它们是相同的 .

    function ingWithinEstablishedParameters() {
        let terOfRecommendation = 'awesome worker!'; //function block scoped
        var sityCheerleading = 'go!'; //function block scoped
    }
    

    块:

    这是区别 . let 仅在 for() 循环中可见, var 对整个函数可见 .

    function allyIlliterate() {
        //tuce is *not* visible out here
    
        for( let tuce = 0; tuce < 5; tuce++ ) {
            //tuce is only visible in here (and in the for() parentheses)
            //and there is a separate tuce variable for each iteration of the loop
        }
    
        //tuce is *not* visible out here
    }
    
    function byE40() {
        //nish *is* visible out here
    
        for( var nish = 0; nish < 5; nish++ ) {
            //nish is visible to the whole function
        }
    
        //nish *is* visible out here
    }
    

    重新声明:

    假设严格模式, var 将允许您在同一范围内重新声明相同的变量 . 另一方面, let 不会:

    'use strict';
    let me = 'foo';
    let me = 'bar'; // SyntaxError: Identifier 'me' has already been declared
    
    'use strict';
    var me = 'foo';
    var me = 'bar'; // No problem, `me` is replaced.
    
  • 10

    如果我正确阅读规范,那么 let thankfully 也可以用来避免用于模拟私有成员的自调用函数 - 一种流行的设计模式,它会降低代码的可读性,使调试变得复杂,不会增加真正的代码保护或其他好处 - 除非可能令人满意某人对语义的渴望,所以停止使用它 . /咆哮

    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
    

    见'Emulating private interfaces'

  • 10

    这是一个添加其他人已经写过的例子 . 假设您要创建一个函数数组 adderFunctions ,其中每个函数都使用一个Number参数,并返回参数和函数索引在数组中的总和 . 尝试使用 var 关键字循环生成 adderFunctions 将无法像某人可能天真期望的那样工作:

    // An array of adder functions.
    var adderFunctions = [];
    
    for (var i = 0; i < 1000; i++) {
      // We want the function at index i to add the index to its argument.
      adderFunctions[i] = function(x) {
        // What is i bound to here?
        return x + i;
      };
    }
    
    var add12 = adderFunctions[12];
    
    // Uh oh. The function is bound to i in the outer scope, which is currently 1000.
    console.log(add12(8) === 20); // => false
    console.log(add12(8) === 1008); // => true
    console.log(i); // => 1000
    
    // It gets worse.
    i = -8;
    console.log(add12(8) === 0); // => true
    

    上面的过程不会生成所需的函数数组,因为 i 的范围超出了创建每个函数的 for 块的迭代 . 相反,在循环结束时,每个函数的闭包中的 i 指的是 adderFunctions 中每个匿名函数的循环结束时的 i 的值(1000) . 这根本不是我们想要的:我们现在在内存中有一个包含1000个不同函数的数组,具有完全相同的行为 . 如果我们随后更新 i 的值,则突变将影响所有 adderFunctions .

    但是,我们可以使用 let 关键字再试一次:

    // Let's try this again.
    // NOTE: We're using another ES6 keyword, const, for values that won't
    // be reassigned. const and let have similar scoping behavior.
    const adderFunctions = [];
    
    for (let i = 0; i < 1000; i++) {
      // NOTE: We're using the newer arrow function syntax this time, but 
      // using the "function(x) { ..." syntax from the previous example 
      // here would not change the behavior shown.
      adderFunctions[i] = x => x + i;
    }
    
    const add12 = adderFunctions[12];
    
    // Yay! The behavior is as expected. 
    console.log(add12(8) === 20); // => true
    
    // i's scope doesn't extend outside the for loop.
    console.log(i); // => ReferenceError: i is not defined
    

    这次, ifor 循环的每次迭代中都会反弹 . 现在,每个函数在函数创建时保持 i 的值,并且 adderFunctions 表现得如预期 .

    现在,图像混合了两种行为,你不建议在同一个脚本中将较新的 letconst 与较旧的 var 混合使用 . 这样做会导致一些令人费解的混乱代码 .

    const doubleAdderFunctions = [];
    
    for (var i = 0; i < 1000; i++) {
        const j = i;
        doubleAdderFunctions[i] = x => x + i + j;
    }
    
    const add18 = doubleAdderFunctions[9];
    const add24 = doubleAdderFunctions[12];
    
    // It's not fun debugging situations like this, especially when the
    // code is more complex than in this example.
    console.log(add18(24) === 42); // => false
    console.log(add24(18) === 42); // => false
    console.log(add18(24) === add24(18)); // => false
    console.log(add18(24) === 2018); // => false
    console.log(add24(18) === 2018); // => false
    console.log(add18(24) === 1033); // => true
    console.log(add24(18) === 1030); // => true
    

    不要让这件事发生在你身上 . 使用短绒 .

    注意:这是一个教学示例,旨在演示循环中的var / let行为以及函数闭包,这也很容易理解 . 这将是添加数字的可怕方式 . 但是在其他环境中的现实世界中可能会遇到在匿名函数闭包中捕获数据的一般技术 . 因人而异 .

  • 3

    以前在JavaScript中只有两个范围,即功能范围和全局范围 . 使用' let '关键字JavaScript现在已经引入了 block-level 变量 .

    要全面了解'let'关键字,ES6: ‘let’ keyword to declare variable in JavaScript会有所帮助 .

  • 1

    看来,至少在Visual Studio 2015中,TypeScript 1.5,“var”允许在块中对同一个变量名进行多次声明,而“let”则不允许 .

    这不会生成编译错误:

    var x = 1;
    var x = 2;
    

    这将:

    let x = 1;
    let x = 2;
    
  • 1

    区别在于每个声明的变量的scope .

    在实践中,范围差异有许多有用的后果:

    • let 变量仅在最近的封闭块( { ... } )中可见 .

    • let 变量仅在声明变量后出现的代码行中可用(即使they are hoisted!) .

    • let 变量可能不会被后续 varlet 重新声明 .

    • 全局 let 变量未添加到全局 window 对象 .

    • let 变量易于使用闭包(它们不会导致race conditions) .

    let 施加的限制会降低变量的可见性,并增加早期发现意外名称冲突的可能性 . 这样可以更容易地跟踪和推理变量,包括它们的reachability(帮助回收未使用的内存) .

    因此,当在大型程序中使用或者以独立开发的框架以新的和意外的方式组合时, let 变量不太可能导致问题 .

    如果您确定在循环中使用闭包(#5)或在代码中声明外部可见的全局变量(#4)时需要单绑定效果,则 var 可能仍然有用 . 如果export从转换器空间迁移到核心语言中,则可以取消使用 var 进行导出 .

    例子

    1. No use outside nearest enclosing block: 此代码块将引发引用错误,因为 x 的第二次使用发生在使用 let 声明的块之外:

    {
        let x = 1;
    }
    console.log(`x is ${x}`);  // ReferenceError during parsing: "x is not defined".
    

    相比之下, var 的相同示例有效 .

    2. No use before declaration:
    在代码可以运行之前,这段代码将抛出 ReferenceError ,因为在声明之前使用了 x

    {
        x = x + 1;  // ReferenceError during parsing: "x is not defined".
        let x;
        console.log(`x is ${x}`);  // Never runs.
    }
    

    相比之下, var 的相同示例在不抛出任何异常的情况下进行分析和运行 .

    3. No redeclaration: 以下代码演示了使用 let 声明的变量以后可能不会重新声明:

    let x = 1;
    let x = 2;  // SyntaxError: Identifier 'x' has already been declared
    

    4. Globals not attached to window:

    var button = "I cause accidents because my name is too common.";
    let link = "Though my name is common, I am harder to access from other JS files.";
    console.log(link);  // OK
    console.log(window.link);  // undefined (GOOD!)
    console.log(window.button);  // OK
    

    5. Easy use with closures: 使用 var 声明的变量不适用于循环内的闭包 . 这是一个简单的循环,它输出变量 i 在不同时间点具有的值序列:

    for (let i = 0; i < 5; i++) {
        console.log(`i is ${i}`), 125/*ms*/);
    }
    

    具体来说,这输出:

    i is 0
    i is 1
    i is 2
    i is 3
    i is 4
    

    在JavaScript中,我们经常在比创建变量时更晚的时间使用变量 . 当我们通过传递给 setTimeout 的闭包延迟输出来证明这一点时:

    for (let i = 0; i < 5; i++) {
        setTimeout(_ => console.log(`i is ${i}`), 125/*ms*/);
    }
    

    ......只要我们坚持使用 let ,输出就会保持不变 . 相反,如果我们使用 var i 代替:

    for (var i = 0; i < 5; i++) {
        setTimeout(_ => console.log(`i is ${i}`), 125/*ms*/);
    }
    

    ...循环意外地输出“我是5”五次:

    i is 5
    i is 5
    i is 5
    i is 5
    i is 5
    
  • 0

    现在我认为使用 let 对一个语句块有更好的变量范围:

    function printnums()
    {
        // i is not accessible here
        for(let i = 0; i <10; i+=)
        {
           console.log(i);
        }
        // i is not accessible here
    
        // j is accessible here
        for(var j = 0; j <10; j++)
        {
           console.log(j);
        }
        // j is accessible here
    }
    

    我认为人们将在这之后开始使用let,以便他们在JavaScript中具有类似的范围,如其他语言,Java,C#等 .

    那些对JavaScript中的作用域不太了解的人过去常犯错误 .

    使用 let 不支持提升 .

    使用此方法,JavaScript中出现的错误将被删除 .

    请参阅ES6 In Depth: let and const以更好地理解它 .

  • 11

    有一些细微的差别 - let 范围表现更像是变量范围,或多或少任何其他语言 .

    例如它适用于封闭块,它们在声明之前不存在,等等 .

    然而值得注意的是 let 只是新Javascript实现的一部分,并且具有不同程度的browser support .

  • -1

    主要区别在于 scope 差异,而 let 只能在其声明的范围内可用,例如在for循环中, var 可以在循环外部访问,例如 . 来自MDN中的文档(也来自MDN的示例):

    let允许您将范围有限的变量声明为使用它的块,语句或表达式 . 这与var关键字不同,var关键字全局定义变量,或者与整个函数本地定义,而不管块范围如何 . let声明的变量的范围是定义它们的块,以及任何包含的子块 . 这样,让我的工作非常像var . 主要区别在于var变量的范围是整个封闭函数:

    function varTest() {
      var x = 1;
      if (true) {
        var x = 2;  // same variable!
        console.log(x);  // 2
      }
      console.log(x);  // 2
    }
    
    function letTest() {
      let x = 1;
      if (true) {
        let x = 2;  // different variable
        console.log(x);  // 2
      }
      console.log(x);  // 1
    }`
    

    在程序和函数的顶层,与var不同,不要在全局对象上创建属性 . 例如:

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

    在块内使用时,将变量的范围限制为该块 . 请注意var之间的区别,其范围在声明它的函数内 .

    var a = 1;
    var b = 2;
    
    if (a === 1) {
      var a = 11; // the scope is global
      let b = 22; // the scope is inside the if-block
    
      console.log(a);  // 11
      console.log(b);  // 22
    } 
    
    console.log(a); // 11
    console.log(b); // 2
    

    也不要't forget it'的ECMA6功能,所以's not fully supported yet, so it'更好总是使用Babel等将其转发给ECMA5 ...有关访问babel website的更多信息

  • 3

    let是es6的一部分 . 这些功能将以简单的方式解释差异 .

    function varTest() {
      var x = 1;
      if (true) {
        var x = 2;  // same variable!
        console.log(x);  // 2
      }
      console.log(x);  // 2
    }
    
    function letTest() {
      let x = 1;
      if (true) {
        let x = 2;  // different variable
        console.log(x);  // 2
      }
      console.log(x);  // 1
    }
    

相关问题