关于javascript:使用”let”和”var”有什么区别?

What's the difference between using “let” and “var”?

ecmascript 6引入了let语句。

我听说它被描述为一个"局部"变量,但我仍然不确定它的行为与var关键字有什么不同。

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


区别在于范围。var的作用域是最近的功能块,let的作用域是最近的封闭块,它可以小于功能块。如果在任何块之外,这两者都是全局的。

另外,使用let声明的变量在其封闭块中声明之前是不可访问的。如演示中所示,这将引发引用错误异常。

演示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
var html = '';

write('#### global ####
'
);
write('globalVar: ' + globalVar); //undefined, but visible

try {
  write('globalLet: ' + globalLet); //undefined, *not* visible
} catch (exception) {
  write('globalLet: exception');
}

write('
set variables'
);

var globalVar = 'globalVar';
let globalLet = 'globalLet';

write('
globalVar: '
+ globalVar);
write('globalLet: ' + globalLet);

function functionScoped() {
  write('
#### function ####'
);
  write('
functionVar: '
+ functionVar); //undefined, but visible

  try {
    write('functionLet: ' + functionLet); //undefined, *not* visible
  } catch (exception) {
    write('functionLet: exception');
  }

  write('
set variables'
);

  var functionVar = 'functionVar';
  let functionLet = 'functionLet';

  write('
functionVar: '
+ functionVar);
  write('functionLet: ' + functionLet);
}

function blockScoped() {
  write('
#### block ####'
);
  write('
blockVar: '
+ 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('
blockVar: '
+ blockVar); // visible here and whole function
  };

  for (let blockLet = 'blockLet', letIndex = 0; letIndex < 1; letIndex++) {
    write('blockLet: ' + blockLet); // visible only here
  };

  write('
blockVar: '
+ 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;
1
[cc lang="javascript"]

.

1
2
</P>全球的:<P>当在函数块外这样使用时,它们非常相似。</P>[cc lang="javascript"]let me = 'go';  // globally scoped
var i = 'able'; // globally scoped

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

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

功能:

当在函数块中这样使用时,它们是相同的。

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

Block:

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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
}

Redeclaration:

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

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


也可以使用let来避免闭包问题。它绑定新值,而不是像下面的示例中所示保留旧引用。

演示

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

上面的代码演示了一个典型的JavaScript关闭问题。对i变量的引用存储在click handler闭包中,而不是i的实际值。

每个单击处理程序都将引用同一个对象,因为只有一个计数器对象可容纳6个对象,所以每次单击都有6个对象。

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

演示(在Chrome和Firefox 50中测试)

1
2
3
4
5
6
'use strict';

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


下面是对let关键字的一些示例的解释。

let works very much like var. The main difference is that the scope of a var variable is the entire enclosing function

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

请注意,只有Mozilla和Chrome浏览器支持它。也就是说,Safari和其他潜在的公司没有。


letvar有什么区别?

  • 使用var语句定义的变量从函数开始就在定义它的整个函数中都是已知的。(*)
  • 使用let语句定义的变量只在定义它的块中知道,从定义它开始。(**)

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
// 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专家凯尔·辛普森写了一篇非常详细的文章,阐述了为什么他认为情况并非如此。

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

  • 如果您正在编写服务器端的javascript代码(node.js),那么可以安全地使用let语句。

  • 如果您正在编写客户端JavaScript代码并使用基于浏览器的蒸腾器(如tracer或babel standalone),则可以安全地使用let语句,但是您的代码在性能方面可能不是最佳的。

  • 如果您正在编写客户端JavaScript代码并使用基于节点的蒸腾器(如tracer shell脚本或babel),则可以安全地使用let语句。而且,因为您的浏览器只知道所发生的代码,所以性能缺陷应该是有限的。

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

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

enter image description here

如何跟踪浏览器支持

有关阅读此答案时哪些浏览器支持let声明的最新概述,请参阅此Can I Use页。

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

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


接受的答案缺少一点:

1
2
3
4
5
{
  let a = 123;
};

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


let块范围

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

在顶层(函数外部)

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

1
2
3
4
5
6
7
8
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的作用域相同。

1
2
3
4
5
6
7
8
9
10
(() => {
  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声明的变量不能在块外访问。

1
2
3
4
5
6
7
8
9
{
  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声明的变量只能在该循环内引用。

1
2
3
4
5
6
7
8
9
10
11
12
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,每次迭代都会得到一个新的变量。这意味着您可以在循环内安全地使用闭包。

1
2
3
4
5
6
7
8
9
// 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声明的变量在声明之前不能被访问。尝试这样做会引发错误。

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

不重新声明

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

1
2
3
4
5
6
7
8
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声明的变量。

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

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

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

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

需要初始值设定项

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

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

有一些细微的区别——let作用域的行为与变量作用域在任何其他语言中的行为或多或少类似。

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

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


下面是这两者之间的区别示例(对chrome的支持刚刚开始):enter image description here

如您所见,var j变量的值仍在for循环作用域(块作用域)之外,但let i变量在for循环作用域之外未定义。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
"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);


  • variable not lifting

    EDOCX1[0]不会提升到它们出现的块的整个范围。相比之下,var可以提升如下。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    {
       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的块范围对于闭包和回收内存的垃圾收集很有用。考虑一下,

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    function process(data) {
        //...
    }

    var hugeData = { .. };

    process(hugeData);

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

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

    但是,块作用域可以使这个巨大的数据结构被垃圾收集。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    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可以将其重新绑定到循环的每个迭代,确保从上一个循环迭代结束时重新为其赋值。考虑一下,

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

    但是,用let替换var

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

    因为let为a)初始化器表达式b)每次迭代(预先计算增量表达式)创建了一个新的词汇环境,所以这里有更多的细节。


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

let allows you to declare variables that are limited in scope to the block, statement, or expression on which it is used. This is unlike the var keyword, which defines a variable globally, or locally to an entire function regardless of block scope.

Variables declared by let have as their scope the block in which they are defined, as well as in any contained sub-blocks. In this way, let works very much like var. The main difference is that the scope of a var variable is the entire enclosing function:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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
}`

At the top level of programs and functions, let, unlike var, does not create a property on the global object. For example:

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

When used inside a block, let limits the variable's scope to that block. Note the difference between var whose scope is inside the function where it is declared.

1
2
3
4
5
6
7
8
9
10
11
12
13
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网站的更多信息


下面是一个例子来补充其他人已经写过的内容。假设您想要创建一个函数数组,adderFunctions,其中每个函数接受一个数字参数,并返回参数和数组中函数索引的总和。尝试使用var关键字通过循环生成adderFunctions,不会像别人可能不知道的那样工作吗?期待:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 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)的i值。这完全不是我们想要的:我们现在在内存中有1000个不同的函数数组,它们的行为完全相同。如果我们随后更新i的值,突变将影响所有adderFunctions

但是,我们可以使用let关键字重试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 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循环的每次迭代中都会反弹。现在,每个函数在函数创建时都保持EDOCX1的值〔18〕,adderFunctions的行为与预期一致。

现在,图像混合这两种行为,您可能会看到为什么不建议在同一脚本中混合较新的letconst和较旧的var。这样做可能会导致一些非常令人困惑的代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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

别让这件事发生在你身上。使用棉绒。

NOTE: This is a teaching example intended to demonstrate the var/let behavior in loops and with function closures that would also be easy to understand. This would be a terrible way to add numbers. But the general technique of capturing data in anonymous function closures might be encountered in the real world in other contexts. YMMV.


差异在每个变量声明的范围内。

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

  • let变量仅在最近的封闭块({ ... }中可见。
  • let变量仅在声明变量后出现的代码行中可用(即使它们被提升!).
  • let变量不能由随后的varlet重新声明。
  • 全局let变量不添加到全局window对象中。
  • let变量易于与闭包一起使用(它们不会导致争用条件)。
  • let所施加的限制降低了变量的可见性,并增加了早期发现意外名称冲突的可能性。这使得跟踪和推理变量变得更容易,包括变量的可访问性(帮助回收未使用的内存)。

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

    如果在循环中使用闭包(5)或在代码中声明外部可见的全局变量(4)时确实需要单一绑定效果,那么var可能仍然有用。如果export从蒸腾空间迁移到核心语言中,则可以替代使用var进行出口。

    实例

    1。最近的封闭块外禁止使用:此代码块将引发引用错误,因为第二次使用x发生在用let声明的块之外:

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

    相比之下,使用var的相同示例是可行的。

    2。申报前不得使用:此代码块将在代码运行之前抛出一个ReferenceError,因为在声明之前使用了x

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

    相反,同一个使用var的示例解析并运行,而不抛出任何异常。

    三。无重新声明:下面的代码演示了使用let声明的变量以后不能重新声明:

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

    4。不附于window的全局:

    1
    2
    3
    4
    5
    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在不同时间点的值序列:

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

    具体来说,该输出:

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

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

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

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

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

    …循环意外输出"i is 5"五次:

    1
    2
    3
    4
    5
    i is 5
    i is 5
    i is 5
    i is 5
    i is 5


    let很有趣,因为它允许我们做类似的事情:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    (() => {
        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]。

    反之

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    (() => {
        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]。


    以下两个功能是否会显示不同之处:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    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
    }


    函数与块范围:

    varlet的主要区别在于,用var声明的变量是函数范围的。而用let声明的函数是块范围的。例如:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    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语句之外访问。该变量footestVar功能范围内的任何地方都可用。

    let的变量:

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

    let变量不会被提升:

    varlet的另一个区别是,用let声明的变量不被提升。举例说明这种行为的最佳方式是:

    带有let的变量不会被提升:

    1
    2
    3
    4
    console.log(letVar);

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

    带有var的变量将被提升:

    1
    2
    3
    4
    console.log(varVar);

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

    全球let不附于window上:

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    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

    When should let be used over var?

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


    看起来,至少在Visual Studio 2015中,typescript 1.5中,"var"允许在一个块中声明多个相同变量名,"let"不允许。

    这不会生成编译错误:

    1
    2
    var x = 1;
    var x = 2;

    这将:

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

    使用let

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

    window对象中不能访问let变量,因为它们不能全局访问。

    1
    2
    3
    4
    5
    6
    7
    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对象中访问。

    1
    2
    3
    4
    5
    6
    7
    function a(){ // this is the Max Scope for var variable
        {
            var x = 12;
        }
        console.log(x);
    }
    a(); // 12

    如果你想知道更多,继续阅读下面的内容

    其中一个最著名的范围访谈问题也可以满足以下对letvar的准确使用;

    使用let

    1
    2
    3
    4
    5
    6
    7
    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

    1
    2
    3
    4
    5
    6
    7
    for (var i = 0; i < 10 ; i++) {
        setTimeout(
            function a() {
                console.log(i); //print 10 times 10
            },
            100 * i);
    }

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


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

    letconst是块范围。

    test.js

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    {
        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还可以用来避免自调用函数来模拟仅限私有成员——一种流行的设计模式,它降低了代码的可读性,使调试复杂化,不增加真正的代码保护或其他好处——除了可能满足某人对语义的需求,所以停止使用它。/咆哮

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    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的黑客:

    1。

    1
    2
    3
    4
        let statistics = [16, 170, 10];
        let [age, height, grade] = statistics;

        console.log(height)

    2。

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

    三。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
        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:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    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)


    让我们成为ES6的一部分。这些函数将以简单的方式解释差异。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    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
    }

    现在我认为使用let将变量更好地限定到一组语句中:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    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
    }

    我认为人们将开始使用"让"在这里,这样他们将在JavaScript中与其他语言、爪哇、C等类似。

    对于javascript中的作用域理解不清楚的人以前常常犯错误。

    使用let不支持吊装。

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

    深入参考ES6:让和const更好地理解它。


    以前,Javascript中只有两个作用域,即函数作用域和全局作用域。使用"let"关键字javascript现在引入了block-level变量。

    要完全理解"let"关键字,es6:javascript中声明变量的"let"关键字将有所帮助。


    本文清楚地定义了var、let和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


    如上所述:

    The difference is scoping. var is scoped to the nearest function
    block and let is scoped to the nearest enclosing block, which
    can be smaller than a function block. Both are global if outside any
    block.Lets see an example:

    实例1:

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

    let keyword

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

    所以区别很简单,也就是范围。


    让我们对比变量,这都是关于范围的。

    var变量是全局变量,基本上可以在任何地方访问,而let变量不是全局变量,只存在于右括号杀死它们之前。

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    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.

    enter image description here

    看看这张图片,我创建了一个非常简单的示例来演示constlet变量。如您所见,当您试图更改const变量时,您将得到错误(试图重写‘name’,它是常量’),但请看let变量…

    首先我们声明let age = 33,然后再指定一些其他值age = 34;,这是可以的,当我们试图改变let变量时,我们没有任何错误。


    由于我目前正试图深入了解JavaScript,我将分享我的简短研究,其中包含一些已经讨论过的伟大的部分,以及其他不同角度的细节。

    如果我们理解函数和块范围之间的区别,那么理解var和let之间的区别就容易了。

    让我们考虑以下情况:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    (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]

    当调用timer()时,将创建一个executionContext,它将包含可变环境和每个迭代对应的所有词汇环境。

    一个简单的例子

    功能范围

    1
    2
    3
    4
    5
    6
    function test() {
        for(var z = 0; z < 69; z++) {
            //todo
        }
        //z is visible outside the loop
    }

    块范围

    1
    2
    3
    4
    5
    6
    function test() {
        for(var z = 0; z < 69; z++) {
            //todo
        }
        //z is not defined :(
    }

    ECMAScript 6又添加了一个关键字来声明变量"const"而不是"let"。

    在"var"之上引入"let"和"const"的主要目标是使用块范围界定,而不是传统的词汇范围界定。本文简要解释了"var"和"let"的区别,并讨论了"const"的含义。


    签入此链接多媒体数字网

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    let x = 1;

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

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

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


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

    在执行上下文的创建阶段,var、let和const仍将其变量存储在内存中,在给定执行上下文的变量环境中具有未定义的值。区别在于执行阶段。如果在给变量赋值之前使用一个用var定义的变量引用,它将只是未定义的。不会引发异常。

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

    1
    2
    3
    4
    5
    6
    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的情况。