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




变量提升 (22)

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
}

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

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


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


接受的答案缺少一点:

{
  let a = 123;
};

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


  • 变量不挂起

    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都被提升了

  • 垃圾收集

    块的范围很有用,涉及闭包和垃圾收集以回收内存。 考虑,

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

    但是,用let替换var

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

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


一些黑客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

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)

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


MDN中检查此链接

let x = 1;

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

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

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

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

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

请参阅' 模拟私有接口 '


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

此外,使用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.

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

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

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


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

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

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

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


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

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


主要区别在于范围差异,而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

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


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

现在我认为使用以下方法可以更好地将变量范围限定为一个语句块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更好地理解它。


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

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

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

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

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


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


letvar什么区别?

  • 从函数的开头,使用var语句定义的变量在其定义的函数中是已知的。 (*)
  • 使用let语句定义的变量仅在定义的块中已知,从定义之后开始。 (**)

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

// 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大师凯尔辛普森了一篇非常精细的文章,说明为什么不是这样

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

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

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

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

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

如何跟踪浏览器支持

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

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

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


功能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对象上时,可以使用它(如果真的有必要,请务必仔细考虑)。


正如刚才提到的:

区别在于范围界定。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

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





let