使用“let”和“var”之间有什么区别?

时间:2009-04-17 20:09:27

标签: javascript scope ecmascript-6 var let

ECMAScript 6介绍了the let statement

我听说它被描述为“本地”变量,但我仍然不太确定它与var关键字的行为有何不同。

有什么区别?应该let何时使用var

40 个答案:

答案 0 :(得分:5276)

区别在于范围界定。 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 : '') + '<br />';
}

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.

答案 1 :(得分:525)

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

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

答案 2 :(得分:144)

letvar之间有什么区别?

  • 使用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是否安全?

有些人会争辩说,将来我们只会使用let语句,而var语句将会过时。 JavaScript大师Kyle Simpson撰写了a very elaborate article on why he believes that won't be the case

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

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

  • 如果您正在编写客户端JavaScript代码并使用基于浏览器的转换器(如Traceurbabel-standalone),则可以安全地使用let语句但是,就性能而言,您的代码可能不是最优的。

  • 如果您正在编写客户端JavaScript代码并使用基于节点的转换器(例如traceur shell scriptBabel),则可以安全地使用let声明。而且因为您的浏览器只会知道转换后的代码,所以应该限制性能缺陷。

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

    仍然有一些浏览器根本不支持let

enter image description here

如何跟踪浏览器支持

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

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

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

答案 3 :(得分:139)

这是explanation of the let keyword的一些例子。

  

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

维基百科上的

This table显示哪些浏览器支持Javascript 1.7。

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

答案 4 :(得分:105)

接受的答案缺少一点:

{
  let a = 123;
};

console.log(a); // ReferenceError: a is not defined

答案 5 :(得分:63)

let

阻止范围

使用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

在函数内部

在函数内部(但在块之外),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声明的变量。

{
  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

constlet非常相似 - 它是块范围的并且具有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

答案 6 :(得分:47)

在大多数基本术语中,

for (let i = 0; i < 5; i++) {
  // i accessible ✔️
}
// i not accessible ❌

for (var i = 0; i < 5; i++) {
  // i accessible ✔️
}
// i accessible ✔️

⚡️沙盒可以玩↓

Edit let vs var

答案 7 :(得分:44)

存在一些微妙的差异 - let范围界定的行为更像是变量范围在或多或少的任何其他语言中。

e.g。它适用于封闭块,它们在声明之前不存在等等。

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

答案 8 :(得分:43)

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

正如您所看到的,var j变量的值仍然在for循环范围之外(Block Scope),但{for循环范围之外的let i变量未定义。

&#13;
&#13;
"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);
&#13;
&#13;
&#13;

答案 9 :(得分:21)

  • 变量未提升

    let不提升到他们出现的整个范围。相比之下,var可以如下所示提升。 < / p>

    {
       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

  • 垃圾收集

    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循环

    循环中的

    let可以将它重新绑定到循环的每次迭代,确保从前一循环迭代结束时重新赋值。考虑,

    // 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

    < / LI>

答案 10 :(得分:20)

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

  

允许您将范围有限的变量声明为使用它的块,语句或表达式。这与 var 关键字不同, var 关键字全局定义变量,或者无论块范围如何,都在本地定义整个函数。

     

let 声明的变量的范围是定义它们的块,以及任何包含的子块。通过这种方式,非常像 var 。主要区别在于 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
  

在程序和功能的顶层, var 不同,不会在全局对象上创建属性。例如:

proj
|---proj
|---media
|---static
|---app
  

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

MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, "media/")

另外不要忘记它的ECMA6功能,所以它还没有完全支持,所以最好总是使用Babel等将它转换为ECMA5 ...有关访问babel website的更多信息

答案 11 :(得分:15)

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

// 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块的迭代。相反,在循环结束时,每个函数的闭包中的ii中为每个匿名函数引用adderFunctions在循环结束时的值(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

不要让这件事发生在你身上。使用linter。

  

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

答案 12 :(得分:13)

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

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

  1. let个变量仅在最近的封闭块({ ... })中可见。
  2. let个变量只能在声明变量之后出现的代码行中使用(即使是they are hoisted!)。
  3. let个变量可能不会被后续varlet重新声明。
  4. 全局let变量未添加到全局window对象。
  5. let变量易于使用带闭包(它们不会导致race conditions)。
  6. let施加的限制会降低变量的可见性,并增加早期发现意外名称冲突的可能性。这样可以更容易地跟踪和推理变量,包括reachability(帮助回收未使用的内存)。

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

    如果在循环中使用闭包(#5)或在代码中声明外部可见的全局变量(#4)时确定需要单绑定效果,那么

    var可能仍然有用。如果export从转换器空间迁移到核心语言中,则可以取代var用于导出。

    实施例

    <强> 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的相同示例在不抛出任何异常的情况下进行解析和运行。

    第3。没有重新声明: 以下代码演示了使用let声明的变量以后可能不会重新声明:

    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声明的变量不适用于循环内的闭包。这是一个简单的循环,它输出变量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*/);
    }
    

    ...循环意外输出&#34;我是5&#34;五次:

    i is 5
    i is 5
    i is 5
    i is 5
    i is 5
    

答案 13 :(得分:11)

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

答案 14 :(得分:11)

功能VS块作用域:

varlet之间的主要区别在于,用var声明的变量是函数作用域的。而用let声明的函数是 block作用域的。例如:

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 

带有var的变量:

当第一个函数testVar被调用时,用var声明的变量foo仍然可以在if语句之外访问。此变量foo将在testVar 功能的范围内随处可见

带有let的变量:

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

let变量不会被悬挂:

varlet之间的另一个区别是用let声明的变量不要被吊起。一个示例是说明此行为的最佳方法:

带有let 不要的变量被吊起:

console.log(letVar);

let letVar = 10;
// referenceError, the variable doesn't get hoisted
带有var

变量被吊起:

console.log(varVar);

var varVar = 10;
// logs undefined, the variable gets hoisted

全局let未附加到window

在全局范围内用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


  

何时应在let上使用var

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

答案 15 :(得分:11)

可能以下两个函数显示差异:

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
}

答案 16 :(得分:7)

ES6引入了两个新关键字( let const )替代 var

当需要块级减速时,可以使用let和const 而不是var。

下表总结了var,let和const之间的区别

enter image description here

答案 17 :(得分:7)

至少在Visual Studio 2015中,TypeScript 1.5,&#34; var&#34;允许在一个块中多次声明相同的变量名,并且&#34; let&#34;没有按&#39;吨

这不会产生编译错误:

var x = 1;
var x = 2;

这将:

let x = 1;
let x = 2;

答案 18 :(得分:6)

如果我正确阅读规范,那么也可以利用let 谢天谢地来避免self invoking functions用于模拟私人会员 - 一种流行的设计模式代码可读性,使调试变得复杂,没有增加真正的代码保护或其他好处 - 除了满足某人对语义的渴望,所以停止使用它。 /咆哮

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

请参阅&#39; Emulating private interfaces&#39;

答案 19 :(得分:6)

使用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

使用var

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

可以在var对象中访问

window个变量,因为它们无法全局访问。

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

如果您想了解更多信息,请继续阅读

关于范围的最着名的访谈问题之一也足以完全使用letvar,如下所示;

使用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时,对于每个循环迭代,变量都是作用域的,并且有自己的副本。

使用var

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

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

答案 20 :(得分:6)

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

答案 21 :(得分:3)

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"

使用let的getter和setter:

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)

答案 22 :(得分:3)

let vs var。全部与范围有关。

var变量是全局的,基本上可以在任何地方访问,而 let变量不是全局的,并且仅在右括号将其杀死之前存在。

请参见下面的示例,并注意lion(let)变量在两个console.logs中的行为不同;它在第二个console.log中超出范围。

var cat = "cat";
let dog = "dog";

var animals = () => {
    var giraffe = "giraffe";
    let lion = "lion";

    console.log(cat);  //will print 'cat'.
    console.log(dog);  //will print 'dog', because dog was declared outside this function (like var cat).

    console.log(giraffe); //will print 'giraffe'.
    console.log(lion); //will print 'lion', as lion is within scope.
}

console.log(giraffe); //will print 'giraffe', as giraffe is a global variable (var).
console.log(lion); //will print UNDEFINED, as lion is a 'let' variable and is now out of scope.

答案 23 :(得分:2)

让我们成为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
}

答案 24 :(得分:1)

我刚遇到一个用例,我不得不在var上使用let来引入新变量。这是一个例子:

我想用动态变量名创建一个新变量。

let variableName = 'a';
eval("let " + variableName + '= 10;');
console.log(a);   // this doesn't work
let variableName = 'a';
eval("var " + variableName + '= 10;');
console.log(a);   // this works

上面的代码不起作用,因为eval引入了一个新的代码块。使用var的声明将在此代码块之外声明一个变量,因为var在函数范围内声明了一个变量。

另一方面,

let在块范围内声明变量。因此,a变量仅在eval块中可见。

答案 25 :(得分:1)

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

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

答案 26 :(得分:1)

如上所述:

  

区别在于范围。 var的作用域是最接近的函数   区块let的作用域为最近的封闭区块,   可以小于功能块。两者都是全局的   块。让我们看一个例子:

示例1

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

let keyword

示例2 : 在第二个示例中,我没有在条件块中使用var关键字,而是使用myvar关键字声明了let。现在,当我打电话给myfunc时,会得到两个不同的输出:myvar=20myvar=10

所以区别很简单,即它的范围。

答案 27 :(得分:1)

现在我认为使用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,这样他们就会像其他语言,Java,C#等在JavaScript中使用类似的范围。

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

使用let不支持提升。

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

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

答案 28 :(得分:1)

enter image description here

看看这张图片,我创建了一个非常简单的示例来演示constlet变量。如您所见,当您尝试更改const变量时,会收到错误消息(试图覆盖常量的“名称” ),但请看一下{{1 }}变量...

首先我们声明let,然后分配其他值let age = 33,这没关系,当我们尝试更改age = 34;变量时没有任何错误

答案 29 :(得分:1)

由于我目前正试图深入了解JavaScript,因此我将分享我的简要研究成果,其中包括已经讨论过的一些很棒的文章,以及从不同的角度来看一些其他细节。

如果我们了解功能块范围了解 var let 之间的区别会更容易>。

让我们考虑以下情况:

this.parent

创建(function timer() { for(var i = 0; i <= 5; i++) { setTimeout(function notime() { console.log(i); }, i * 1000); } })(); Stack VariableEnvironment //one VariablEnvironment for timer(); // when the timer is out - the value will be the same value for each call 5. [setTimeout, i] [i=5] 4. [setTimeout, i] 3. [setTimeout, i] 2. [setTimeout, i] 1. [setTimeout, i] 0. [setTimeout, i] #################### (function timer() { for (let i = 0; i <= 5; i++) { setTimeout(function notime() { console.log(i); }, i * 1000); } })(); Stack LexicalEnvironment - each iteration has a new lexical environment 5. [setTimeout, i] [i=5] LexicalEnvironment 4. [setTimeout, i] [i=4] LexicalEnvironment 3. [setTimeout, i] [i=3] LexicalEnvironment 2. [setTimeout, i] [i=2] LexicalEnvironment 1. [setTimeout, i] [i=1] LexicalEnvironment 0. [setTimeout, i] [i=0] 时会创建一个 ExecutionContext ,其中将包含 VariableEnvironment 和与每个迭代相对应的所有 LexicalEnvironments

还有一个简单的例子

功能范围

timer()

块范围

function test() {
    for(var z = 0; z < 69; z++) {
        //todo
    }
    //z is visible outside the loop
}

答案 30 :(得分:1)

我认为这些术语和大多数示例都有些让人不知所措, 我个人与之不同的主要问题是了解什么是“阻止”。 在某种程度上,我意识到,除了IF语句以外,任何其他花括号都可能是大括号。 函数或循环的左括号{将定义一个新块,其中用let定义的任何内容在同一事物(函数或函数的右括号}之后将不可用。环); 考虑到这一点,更容易理解:

let msg = "Hello World";

function doWork() { // msg will be available since it was defined above this opening bracket!
  let friends = 0;
  console.log(msg);

  // with VAR though:
  for (var iCount2 = 0; iCount2 < 5; iCount2++) {} // iCount2 will be available after this closing bracket!
  console.log(iCount2);
  
    for (let iCount1 = 0; iCount1 < 5; iCount1++) {} // iCount1 will not be available behind this closing bracket, it will return undefined
  console.log(iCount1);
  
} // friends will no be available after this closing bracket!
doWork();
console.log(friends);

答案 31 :(得分:1)

本文明确定义了var,let和const

之间的区别
  

const表示不会重新分配标识符。

     

let,是一个可以重新分配变量的信号,例如a   循环中的计数器,或算法中的值交换。它也发出信号   变量将仅在其定义的块中使用,   这并不总是包含整个函数。

     

var现在是定义变量时可用的最弱信号   在JavaScript中。该变量可能会或可能不会被重新分配,而且   变量可以用于也可以不用于整个函数,或仅用于   块或循环的目的。

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

答案 32 :(得分:0)

通过理解“为什么在JavaScript中使用”,可以很容易理解。在Link中,您可以在 JavaScript 中找到有关关键字的更多有用信息。

根据定义, let 允许您声明限于block语句或使用该语句的表达式的变量,与var关键字不同,该关键字定义了一个变量全局或局部到整个功能,而不管块范围如何。 var和let之间的另一个区别是,后者只有在解析器对其求值时才会初始化为value(请参见下文)。

就像const一样,当全局声明时(在最顶层的作用域中),let不会创建窗口对象的属性。

如下例所示:-let语句声明一个块作用域局部变量,可以选择将其初始化为一个值。

let x = 1;

if (x === 1) {
 let x = 2;

 console.log(x);
 // expected output: 2
}

  console.log(x);
  // expected output: 1

let 声明的变量的范围在定义它们的块以及任何包含的子块中。这样,让我们​​非常像var。 主要区别在于var变量的范围是整个封闭函数: 我们可以通过以下示例了解它:-

function varTest() {
 var x = 1;
 {
  var x = 2;  // same variable!
  console.log(x);  // 2
 }
  console.log(x);  // 2
}

function letTest() {
 let x = 1;
 {
  let x = 2;  // different variable
  console.log(x);  // 2
 }
  console.log(x);  // 1
}

在程序和功能的顶层,与 var 不同, let 不会在全局对象上创建属性。 例如:

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

在处理构造函数时,可以使用let绑定来共享一个或多个私有成员,而无需使用闭包:示例:-

var Thing;
 {
      let privateScope = new WeakMap();
      let counter = 0;
    
      Thing = function() {
        this.someProperty = 'foo';
        
        privateScope.set(this, {
          hidden: ++counter,
        });
      };
    
      Thing.prototype.showPublic = function() {
        return this.someProperty;
      };
    
      Thing.prototype.showPrivate = function() {
        return privateScope.get(this).hidden;
      };
    }
    
    console.log(typeof privateScope);
    // "undefined"
    
    var thing = new Thing();
    
    console.log(thing);
    // Thing {someProperty: "foo"}
    
    thing.showPublic();
    // "foo"
    
    thing.showPrivate();
    // 1

可以使用var创建具有局部变量闭包的相同隐私模式,但是它们需要函数作用域(通常是模块模式中的IIFE),而不仅仅是上面示例中的块作用域。

JavaScript 中为块范围变量声明选择了

'let'吗?在全局范围中未定义。

JavaScript 从一开始就具有var,因此他们只需要另一个关键字,就从其他数十种语言中借用了这些语言,这些语言已将let作为尽可能接近var的传统关键字使用,尽管在JavaScript让我们改为创建块范围局部变量。

它与 var 的作用完全相同,但范围有所不同。现在,它不能使用 var 这个名称,因为该名称已经被使用。

因此,看起来它是在具有吸引力的英语语言结构中具有语义的次佳名称。

  let mySon = 'Peter';

用英语说“让我儿子叫彼得”。

这就是 JavaScript 使用let关键字与其他语言程序员一起使用时更加灵活的原因。它用于升级语言。

答案 33 :(得分:0)

在2015年之前,使用var关键字是声明 JavaScript 变量的唯一方法。

ES6 (JavaScript版本)之后,它允许2个新的关键字 let const

let =可以重新分配
const =无法重新分配(const,它来自常量的短格式'const')

示例:

  • 假设在这里声明一个国家名/您的母亲名const是最合适的。因为很少有机会早晚更改国家名称或您的母亲名称。

  • 您的年龄,体重,薪水,自行车速度以及更多类似这些经常更改或需要重新分配的数据类型。在这种情况下,使用let

答案 34 :(得分:0)

下面显示了'let'和'var'在范围上的区别:

let gfoo = 123;
if (true) {
    let gfoo = 456;
}
console.log(gfoo); // 123

var hfoo = 123;
if (true) {
    var hfoo = 456;
}
console.log(hfoo); // 456

gfoo定义的let最初位于全局范围中,当我们再次在gfoo内声明if clause时,其< strong> 作用域已更改 ,并且将新值分配给该范围内的变量时,它不会影响全局范围。

hfoo定义的var最初位于全局范围中,但是当我们在if clause中声明它时,它再次考虑了全局范围hfoo,尽管再次使用var声明它。当我们重新分配它的值时,我们看到全局范围hfoo也受到了影响。这是主要区别。

答案 35 :(得分:0)

我想将这些关键字链接到执行上下文,因为执行上下文在所有这些方面都很重要。执行上下文具有两个阶段:创建阶段和执行阶段。此外,每个执行上下文都具有可变环境和外部环境(其词法环境)。

在执行上下文的创建阶段,var,let和const仍将其变量存储在给定执行上下文的变量环境中的未定义值。区别在于执行阶段。如果在分配值之前使用引用使用var定义的变量,则该变量将是未定义的。没有例外。

但是,在声明之前,不能引用用let或const声明的变量。如果尝试在声明它之前使用它,则在执行上下文的执行阶段将引发异常。现在,根据执行上下文的创建阶段,该变量仍将保留在内存中,但是引擎不允许您使用它:

function a(){
    b;
    let b;
}
a();
> Uncaught ReferenceError: b is not defined

使用var定义的变量,如果引擎无法在当前执行上下文的变量环境中找到该变量,则它将向上作用域链(外部环境)并检查该变量的外部环境的变量环境。如果在此处找不到它,它将继续搜索范围链。 let和const并非如此。

let的第二个特点是它引入了块作用域。块由花括号定义。示例包括功能块,if块,for块等。当在块内部使用let声明变量时,该变量仅在块内部可用。实际上,每次运行该块时,例如在for循环中,它将在内存中创建一个新变量。

ES6还引入了const关键字来声明变量。 const也是块作用域的。 let和const之间的区别在于,必须使用初始化程序声明const变量,否则它将产生错误。

最后,当涉及执行上下文时,使用var定义的变量将附加到“ this”对象。在全局执行上下文中,它将是浏览器中的窗口对象。 let或const并非如此。

答案 36 :(得分:-1)

var 变量的作用域是函数作用域和全局作用域,而 let 变量作用域是块作用域(大括号 {})

const myFun=()=> {
      var str1 = "hello";
      let str2 = "program";
    
      console.log(str1, str2); // hello program
    
      {
        var myvar1 = "hiii"
        let myvar2 = "ooo";
        console.log(myvar1, myvar2); // hiii ooo
      }
    
      console.log(myvar1); // hiii
      console.log(myvar2); // ReferenceError
    }
    console.log(myvar1); // not defined
    myFun();

答案 37 :(得分:-1)

仅当您想在脚本中将变量设置为全局变量或想在同一范围内重新声明相同的变量时,才使用 var 关键字。 随着 ES2015 的到来,当您想将变量设置为函数作用域、块作用域、循环作用域或不想在同一作用域内重新声明变量时,请使用 let 关键字。

答案 38 :(得分:-1)

检查此链接 MDN

let x = 1;

if (x === 1) {
let x = 2;

console.log(x);
// expected output: 2
}

console.log(x);
// expected output: 1

答案 39 :(得分:-1)

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"