使用“let”和“var”声明一个variables有什么区别?

ECMAScript 6引入了let语句 。 我听说它被描述为一个“本地”variables,但我还不太确定它的行为与var关键字不同。

有什么区别? 什么时候应该let var

区别在于范围。 var的范围是最接近的function块, let范围最接近的封闭块,它可以比function块小。 如果在任何区块之外,两者都是全局

另外,使用let声明的variables在封闭块中声明之前是不可访问的。 如演示中所示,这将引发ReferenceErrorexception。

演示 :

 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('functionLet: 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('functionLet: exception'); } } function write(line) { html += (line ? line : '') + '<br />'; } functionScoped(); blockScoped(); document.getElementById('results').innerHTML = html; 
 <pre id="results"></pre> 

let也可以用来避免closures的问题。 它具有新鲜的价值,而不是像下面的例子那样保留旧的参考。

DEMO

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

以上代码演示了一个经典的JavaScriptclosures问题。 对ivariables的引用存储在click处理程序闭包中,而不是i的实际值。

每个单击处理程序都会引用同一个对象,因为只有一个计数器对象可以保存6个,所以每次点击都可以得到6个。

一般的解决方法是将其包装在一个匿名函数中,并将其作为parameter passing给i 。 这样的问题也可以通过使用let代替var来避免,如下面的代码所示。

DEMO (在Chrome和Firefox 50中testing)

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

下面是关于let关键字的一些解释 。

让作品非常像var。 主要区别在于varvariables的范围是整个封闭函数

维基百科上的这张表显示哪些浏览器支持Javascript 1.7。

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

接受的答案是缺less一点:

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

letvar什么区别?

  • 从定义的那一刻开始,使用var语句定义的variables在其定义的函数中是已知
  • 一个使用let语句定义的variables只有在它被定义的时候才知道。

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

 function loop(arr) { // i IS NOT known here // j IS NOT known here for( var i = 0; i < arr.length; i++ ) { // i IS known here } // i IS known here // j IS NOT known here for( let j = 0; j < arr.length; j++ ) { // j IS known here } // i IS known here // j IS NOT known here } 

在这里,我们可以看到我们的variablesj只在第一个for循环中被知道,而不是在之前和之后。 然而,我们的variablesi从它被定义的那一刻起就被称为整个函数。


今天使用let是否安全?

有些人会说,将来我们只会使用let语句,var语句将会过时。 JavaScript大师凯尔·辛普森Kyle Simpson)了一篇非常详尽的文章,说明为什么事实并非如此

然而今天,情况绝非如此。 实际上,我们实际上需要问自己使用let语句是否安全。 这个问题的答案取决于你的环境:

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

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

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

    今天,2016年2月23日,这些是一些浏览器,不支持let或只有部分支持:

    • Internet Explorer 10及以下版本(不支持)
    • Firefox 43及以下版本(不支持)
    • Safari 9及以下版本(不支持)
    • Opera Mini 8及以下版本(不支持)
    • Android浏览器4及以下(不支持)
    • 歌剧36及以下(部分支持)
    • Chrome 51及以下(部分支持)

在这里输入图像说明


如何跟踪浏览器的支持

有关阅读本答案时哪些浏览器支持let语句的最新概述,请参阅Can I Use页面

以下是两者之间差异的一个例子(支持刚开始的chrome): 在这里输入图像描述

正如你所看到的, var jvariables仍然有一个超出for循环范围的值,但let ivariables在for循环范围之外是未定义的。

有一些细微的差异 – let范围界定更像是在其他语言或多或less的语言中进行variables范围界定。

例如它的范围是封闭的,它们在声明之前是不存在的,等等。

不过值得一提的是, let只是新的Javascript实现的一部分,并且有不同程度的浏览器支持 。

let

块范围

使用let关键字声明的variables是块范围的,这意味着它们只在声明它的块中可用。

在顶层(function之外)

在顶层,使用let声明的variables不会在全局对象上创build属性。

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

在一个函数里面

在一个函数里面(但是在一个块之外), let有和var一样的范围。

 (() => { 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声明的variables不能在该块之外访问。

 { 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循环声明的variables只能在该循环中引用。

 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 ,每次迭代都会得到一个新的variables。 这意味着你可以安全地在循环中使用闭包。

 // 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); } 

颞区死亡

由于时间死区 ,使用let声明的variables在声明之前不能被访问。 试图这样做会引发错误。

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

不重新宣布

您不能使用let多次声明相同的variables。 你也不能声明一个variables,使用与使用var声明的另一个variables相同的标识符。

 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的块范围和TDZ。 然而,有两件事情是不同的。

不重新分配

使用const声明的variables不能被重新赋值。

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

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

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

如果你想有一个不可变的对象,你应该使用Object.freeze()

初始化程序是必需的

在使用const声明一个variables时,你总是必须指定一个值。

 const a; // SyntaxError: Missing initializer in const declaration 
  • variables不提升

    let 不要葫芦在他们出现的区块的整个范围。相比之下, var可以提升如下。

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

    实际上,Per @Bergi, varlet都是悬挂的 。

  • 垃圾收集

    let块的范围是有用的closures和垃圾回收来回收内存。 考虑,

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

    click处理程序callback根本不需要hugeDatavariables。 理论上,经过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循环

    let循环可以将它重新绑定到循环的每个迭代,确保从前一个循环迭代的末尾重新赋值。 考虑,

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

    但是,用letreplacevar

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

    因为let创build一个新的词法环境与这些名称为a)初始化expression式b)每次迭代(以前评估增量expression式),更多的细节在这里 。

这里有一个例子可以添加到别人已经写的东西上。 假设你想创build一个函数数组adderFunctions ,其中每个函数接受一个Number参数,并返回数组中参数和函数索引的和。 尝试使用var关键字生成adderFunctions带有循环的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的作用域超出了创build每个函数的for块的迭代范围。 相反,在循环结束时,每个函数的闭包中的iadderFunctions每个匿名函数的循环结尾(1000)处的i值。 这不是我们想要的:我们现在在内存中有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循环的每次迭代中都会反弹。 现在每个函数都在创build函数时保留了i的值,而adderFunctions行为与预期相同。

现在,混合这两种行为的图像,你可能会明白为什么不build议在同一个脚本中混合新的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行为,以及函数闭包,这些也很容易理解。 这将是一个可怕的方式来添加数字。 但是在匿名函数closures中捕获数据的一般技术可能会在现实世界中遇到。 因人而异。

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

 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 } 

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]。

区别在于每个variables声明的范围 。

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

  1. letvariables只在最近的封闭块( { ... } )中可见。
  2. letvariables只能在variables被声明发生的代码行中使用(即使它们被挂起了 !)。
  3. letvariables不能被随后的varlet重新声明。
  4. 全局letvariables不被添加到全局window对象。
  5. letvariables很容易与闭包一起使用 (它们不会导致竞争条件 )。

设置的限制会降低variables的可见性,并增加发生意外名称冲突的可能性。 这使得更容易跟踪和推理variables,包括它们的可达性 (帮助回收未使用的内存)。

因此, letvariables在大型程序中使用时或在独立开发的框架以新的和意想不到的方式组合时不太可能导致问题。

如果您确定在循环中使用闭包(#5)或在代码中声明外部可见的全局variables(#4)时确实需要单一绑定效果,则var仍然可能很有用。 如果export从transpiler空间迁移到核心语言,则可以使用var代替export

例子

1.在最近的封闭块之外不使用:这个代码块会抛出一个引用错误,因为第二次使用x出现在使用let声明的块之外:

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

相反,与var例子相同的例子。

2.申报前不用:
这段代码在代码运行之前会抛出一个ReferenceError ,因为在声明之前使用了x

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

相比之下,与var相同的例子parsing并运行而不抛出任何exception。

3.不重新声明:以下代码表明使用let声明的variables可能以后不会被重新声明:

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

4.全球不附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.容易使用闭包:var声明的variables在闭合循环内不能很好地工作。 这是一个简单的循环,输出variablesi在不同时间点的值序列:

 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中,我们经常在比创build时更明显的时间使用variables。 当我们用一个传递给setTimeout的闭包来延迟输出来certificate这一点时:

 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 

主要的区别在于Scope的区别, let只能在声明的范围内使用,比如在for循环中, var可以在循环之外访问。 从MDN中的文档( MDN中的例子):

let允许你将范围受限的variables声明为使用它的块,语句或expression式。 这与var关键字不同, var关键字全局定义variables,或者在本地定义整个函数,而不考虑块范围。

let声明的variables的作用范围是它们被定义的区块,以及任何包含的子区块。 这样,就像var一样工作。 主要区别在于varvariables的范围是整个封闭函数:

 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不同,我们不会在全局对象上创build一个属性。 例如:

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

当在块内部使用时,让variables的作用域限制在该块中。 注意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 

也不要忘记它的ECMA6function,所以还没有完全支持,所以最好总是使用Babel等将其转换为ECMA6 …有关访问babel网站的更多信息

It also appears that, at least in Visual Studio 2015, TypeScript 1.5, "var" allows multiple declarations of the same variable name in a block, and "let" doesn't.

This won't generate a compile error:

 var x = 1; var x = 2; 

这会:

 let x = 1; let x = 2; 

If I read the specs right then let thankfully can also be leveraged to avoid self invoking functions used to simulate private only members – a popular design pattern that decreases code readability, complicates debugging, that adds no real code protection or other benefit – except maybe satisfying someone's desire for semantics, so stop using it. /rant

 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 

See ' Emulating private interfaces '

var is global scope (hoist-able) variable.

let and const is block scope.

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 

Previously there were only two scopes in JavaScript, ie functional and global. With ' let ' keyword JavaScript has now introduced block-level variables.

To have a complete understanding of the 'let' keyword, ES6: 'let' keyword to declare variable in JavaScript will help.

Some hacks with 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 and 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) 

Now I think there is better scoping of variables to a block of statements using 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 } 

I think people will start using let here after so that they will have similar scoping in JavaScript like other languages, Java, C#, etc.

People with not a clear understanding about scoping in JavaScript used to make the mistake earlier.

Hoisting is not supported using let .

With this approach errors present in JavaScript are getting removed.

Refer to ES6 In Depth: let and const to understand it better.

ECMAScript 6 added one more keyword to declare variables other the "const" other than "let".

The primary goal of introduction of "let" and "const" over "var" is to have block scoping instead of traditional lexical scoping. This article explains very briefly difference between "var" and "let" and it also covers the discussion on "const" .

This article clearly defines the difference between var, let and const

const is a signal that the identifier won't be reassigned.

let , is a signal that the variable may be reassigned, such as a counter in a loop, or a value swap in an algorithm. It also signals that the variable will be used only in the block it's defined in, which is not always the entire containing function.

var is now the weakest signal available when you define a variable in JavaScript. The variable may or may not be reassigned, and the variable may or may not be used for an entire function, or just for the purpose of a block or loop.

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