javascript - 为什么用let不用var - 变量提升




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

let

阻止范围

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

在顶层(功能之外)

在顶层,使用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 in循环声明的变量只能在该循环内引用。

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

时间死区

由于时间死区 ,使用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

ECMAScript 6引入let语句 。 我听说它被描述为“本地”变量,但我仍然不太确定它与var关键字的行为方式有何不同。

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


功能VS块范围:

var和之间的主要区别在于let声明的变量var函数作用域。声明的函数let块作用域。例如:

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被调用时,声明的变量foo var仍然可以在if语句外部访问。这个变量foo将可无处不在的范围内的testVar 功能

变量let

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

let 变量不会被挂起:

var和之间的另一个区别let是声明的变量let 没有被提升。一个示例是说明此行为的最佳方式:

let 挂起的变量:

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不接受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

尽可能使用letvar因为它的范围更加具体。这减少了在处理大量变量时可能发生的潜在命名冲突。var当你想要一个显式的全局变量在window对象上时,可以使用它(如果真的有必要,请务必仔细考虑)。



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


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

let并且const是块范围。

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


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


以下是let关键字解释和一些示例。

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

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

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


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

正如您所看到的, var j变量的值仍然在for循环范围之外(Block Scope),但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);


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

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范围表达更像是变量范围在或多或少任何其他语言中。

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

但是值得注意的是, let只是新Javascript实现的一部分,并且具有不同程度的浏览器支持


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

const 是一个信号,标识符不会被重新分配。

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

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

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


正如刚才提到的:

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

例1:

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

示例2 在我的第二个示例中var,我使用关键字而不是在条件块中myvar使用let关键字。现在,当我打电话时,myfunc我得到两个不同的输出:myvar=20myvar=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每个匿名函数的循环结束时的值(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行为以及函数闭包,这也很容易理解。 这将是添加数字的可怕方式。 但是在其他环境中的现实世界中可能会遇到在匿名函数闭包中捕获数据的一般技术。 因人而异。


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
}

不同之处在于使用每个变量声明的变量的scope

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

  1. let变量仅在最近的封闭块({ ... })中可见。
  2. let变量只能在声明变量出现的代码行中使用(即使它们被挂起!)。
  3. let变量可能不会被后续var或后续重新声明let
  4. 全局let变量不会添加到全局window对象中。
  5. let变量易于使用闭包(它们不会导致竞争条件)。

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

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

var如果在循环中使用闭包(#5)或在代码中声明外部可见的全局变量(#4)时确定需要单绑定效果,那么仍然有用。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.
}

相比之下,使用varparses和run 的相同示例没有抛出任何异常。

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

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

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

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

要完全理解'let'关键字,ES6:'let'关键字在JavaScript中声明变量会有所帮助。


如果我读的规范正确,然后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

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:让和const更好地理解它。





let