ECMAScript 6引入了let
语句 。
我听说它被描述为“局部”变量,但是我仍然不太确定它的行为与var
关键字有何不同。
有什么区别? 什么时候应该let
在使用var
?
#1楼
范围规则
主要区别是作用域规则。 var
关键字声明的变量的作用域范围是立即函数主体(因此,函数作用域),而let
变量的作用域范围是由{ }
表示的直接封闭块(因此,块作用域)。
function run() {
var foo = "Foo";
let bar = "Bar";
console.log(foo, bar);
{
let baz = "Bazz";
console.log(baz);
}
console.log(baz); // ReferenceError
}
run();
将let
关键字引入该语言的原因是函数范围令人困惑,并且是JavaScript中错误的主要来源之一。
从另一个stackoverflow问题看这个示例:
var funcs = [];
// let's create 3 functions
for (var i = 0; i < 3; i++) {
// and store them in funcs
funcs[i] = function() {
// each should log its value.
console.log("My value: " + i);
};
}
for (var j = 0; j < 3; j++) {
// and now let's run each one to see
funcs[j]();
}
My value: 3
每次funcs[j]();
都输出到控制台My value: 3
funcs[j]();
由于匿名函数绑定到同一变量,因此被调用。
人们必须创建立即调用的函数以从循环中捕获正确的值,但这也很麻烦。
吊装
尽管使用var
关键字声明的变量被“提升”到块的顶部,这意味着即使在声明它们之前,也可以在其封闭范围内访问它们:
function run() {
console.log(foo); // undefined
var foo = "Foo";
console.log(foo); // Foo
}
run();
在评估其定义之前,不初始化let变量。 在初始化之前访问它们会导致ReferenceError
。 从块的开始到初始化处理之前,变量都处于“临时死区”。
function checkHoisting() {
console.log(foo); // ReferenceError
let foo = "Foo";
console.log(foo); // Foo
}
checkHoisting();
创建全局对象属性
在顶层, let
与var
不同,不会在全局对象上创建属性:
var foo = "Foo"; // globally scoped
let bar = "Bar"; // globally scoped
console.log(window.foo); // Foo
console.log(window.bar); // undefined
重新声明
在严格模式下, var
使您可以在同一范围内重新声明相同的变量,而let
引发SyntaxError。
'use strict';
var foo = "foo1";
var foo = "foo2"; // No problem, 'foo' is replaced.
let bar = "bar1";
let bar = "bar2"; // SyntaxError: Identifier 'bar' has already been declared
#2楼
这是一个示例,可以补充其他人已经写的内容。 假设您要创建一个函数数组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
在每个功能的关闭是指i
的值在环路(1000),用于在每一个匿名函数末尾adderFunctions
。 这根本不是我们想要的:我们现在在内存中拥有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
这次, i
在for
循环的每次迭代中反弹。 现在,每个函数在创建函数时都会保留i
的值,并且adderFunctions
行为符合预期。
现在,对两种行为进行图像混合,您可能会明白为什么不建议在同一脚本中将较新的let
和const
与较旧的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
行为以及具有易于理解的函数闭包。 这将是添加数字的糟糕方法。 但是在其他上下文中,在现实世界中可能会遇到在匿名函数闭包中捕获数据的通用技术。 YMMV。
#3楼
这是两者之间差异的示例(刚刚开始支持chrome):
如您所见, var j
变量的值仍在for循环范围(Block Scope)之外,但在for循环范围之外, let i
变量未定义。
"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);
#4楼
let
也可以用来避免关闭问题。 它将绑定新的值,而不是保留旧的参考,如下面的示例所示。
for(var i=1; i<6; i++) { $("#div" + i).click(function () { console.log(i); }); }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <p>Clicking on each number will log to console:</p> <div id="div1">1</div> <div id="div2">2</div> <div id="div3">3</div> <div id="div4">4</div> <div id="div5">5</div>
上面的代码演示了经典的JavaScript关闭问题。 对i
变量的引用存储在单击处理程序闭包中,而不是i
的实际值中。
每个单击处理程序都将引用同一对象,因为只有一个计数器对象包含6个,因此每次单击将获得6个。
一个通用的解决方法是将其包装在一个匿名函数中,并将i
作为参数传递。 现在也可以通过使用let
代替var
来避免此类问题,如下面的代码所示。
(在Chrome和Firefox 50中测试)
for(let i=1; i<6; i++) { $("#div" + i).click(function () { console.log(i); }); }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <p>Clicking on each number will log to console:</p> <div id="div1">1</div> <div id="div2">2</div> <div id="div3">3</div> <div id="div4">4</div> <div id="div5">5</div>
#5楼
可接受的答案遗漏了一点:
{
let a = 123;
};
console.log(a); // ReferenceError: a is not defined
#6楼
看起来,至少在Visual Studio 2015 TypeScript 1.5中,“ var”允许在块中使用相同变量名的多个声明,而“ let”则不允许。
这不会产生编译错误:
var x = 1;
var x = 2;
这会:
let x = 1;
let x = 2;
#7楼
可能以下两个功能显示出差异:
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
}
#8楼
可变不吊装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, 都吊起了
var
和let
。垃圾收集
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
in the loop可以将其重新绑定到循环的每次迭代,并确保从上一次循环迭代结束时重新为其分配值。 考虑,// 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)每次迭代(以前是评估增量表达式)的名称创建一个新的词法环境,所以这里有更多详细信息。
#9楼
以前,JavaScript中只有两个作用域,即功能性和全局性。 现在,使用' let
'关键字,JavaScript引入了block-level
变量。
为了全面了解'let'关键字, ES6:'let'关键字在JavaScript中声明变量将很有帮助。
#10楼
let
和var
什么区别?
要了解差异,请考虑以下代码:
// 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)写了一篇非常详细的文章,阐述了为什么他认为情况并非如此 。
但是,今天绝对不是这种情况。 实际上,我们实际上需要自问,使用let
语句是否安全。 该问题的答案取决于您的环境:
如果您正在编写服务器端JavaScript代码( Node.js ),则可以安全地使用
let
语句。如果您正在编写客户端JavaScript代码并使用基于浏览器的编译器(例如Traceur或babel-standalone ),则可以安全地使用
let
语句,但是就性能而言,代码可能不是最佳选择。如果您正在编写客户端JavaScript代码并使用基于Node的编译器(例如traceur shell脚本或Babel ),则可以安全地使用
let
语句。 并且由于您的浏览器仅会知道已转译的代码,因此应限制性能方面的弊端。如果您正在编写客户端JavaScript代码并且不使用翻译器,则需要考虑浏览器支持。
仍然有一些根本不支持
let
浏览器:
如何跟踪浏览器支持
有关在阅读此答案时哪些浏览器支持let
语句的最新概述,请参见Can I Use
页面 。
(*)因为提升了 JavaScript变量,所以可以在声明它们之前初始化和使用全局范围和功能范围的变量。 这意味着声明始终在作用域的顶部。
(**)不提升块范围的变量
#11楼
现在,我认为使用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深度:let和const可以更好地理解它。
#12楼
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]。
#13楼
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)
#14楼
如果我读的规范正确的,然后let
幸运的是还可以利用,以避免自我调用函数用来模拟私有只成员- 即降低代码的可读性流行的设计模式,复杂化的调试,这增加了没有真正的代码保护或其他好处-也许除了满足某人对语义的渴望,因此请停止使用它。 / rant
var SomeConstructor;
{
let privateScope = {};
SomeConstructor = function SomeConstructor () {
this.someProperty = "foo";
privateScope.hiddenProperty = "bar";
}
SomeConstructor.prototype.showPublic = function () {
console.log(this.someProperty); // foo
}
SomeConstructor.prototype.showPrivate = function () {
console.log(privateScope.hiddenProperty); // bar
}
}
var myInstance = new SomeConstructor();
myInstance.showPublic();
myInstance.showPrivate();
console.log(privateScope.hiddenProperty); // error
请参阅“ 模拟专用接口 ”
#15楼
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
函数内部
在函数内部(但在块外部), 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
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
非常相似,它具有块作用域并具有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
#16楼
本文明确定义了var,let和const之间的区别
const
是一个信号,表明不会重新分配标识符。
let
是信号,该变量可以重新分配,例如循环中的计数器或算法中的值交换。 它还表明该变量将仅在其定义的块中使用,而该块并不总是整个包含函数。现在,当您在JavaScript中定义变量时,
var
是最弱的信号。 该变量可能会也可能不会被重新分配,并且该变量可能会或可能不会用于整个功能,或者仅出于块或循环的目的。
https://medium.com/javascript-scene/javascript-es6-var-let-or-const-ba58b8dcde75#.esmkpbg9b
#17楼
主要区别在于范围的区别,而let只能在声明的范围内使用,例如在for循环中,例如var可以在循环外部访问。 从MDN中的文档中(也有MDN的示例):
let允许您声明范围仅限于使用它的块,语句或表达式的变量。 这与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
}`
在程序和函数的顶层, let与var不同,不会在全局对象上创建属性。 例如:
var x = 'global';
let y = 'global';
console.log(this.x); // "global"
console.log(this.y); // undefined
在块内使用时,让该变量的作用域限制在该块内。 请注意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
另外,别忘了它的ECMA6功能,因此尚未得到完全支持,因此最好始终使用Babel等将其转换为ECMA5。有关访问babel网站的更多信息
#18楼
区别在于每个变量声明的变量的范围 。
实际上,范围差异会带来许多有用的后果:
-
let
变量仅在其最近的封闭块({ ... }
)中可见。 -
let
变量仅在声明该变量后发生的代码行中可用(即使它们已吊起 !)。 -
let
变量不能由后续的var
或let
声明。 - 全局
let
变量不会添加到全局window
对象。 -
let
变量易于与闭包一起使用 (它们不会引起竞争条件 )。
let
施加的限制降低了变量的可见性,并增加了及早发现意外的名称冲突的可能性。 这样可以更轻松地跟踪和推断变量,包括变量的可达性 (帮助回收未使用的内存)。
因此,当在大型程序中使用或以新的出乎意料的方式组合独立开发的框架时, 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 = 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*/);
}
...循环意外输出“ i为5”五次:
i is 5
i is 5
i is 5
i is 5
i is 5
#19楼
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
#20楼
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
}
#21楼
当使用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
ES5中的var
和variables在函数中具有作用域,这意味着变量在函数内有效,而在函数本身外无效。
可以在window
对象中访问var
变量,因为它们不能全局访问。
function a(){ // this is the Max Scope for var variable
{
var x = 12;
}
console.log(x);
}
a(); // 12
如果您想了解更多,请继续阅读下面的内容
关于范围的最著名的访谈问题之一也可以满足let
和var
的确切用法,如下所示;
当使用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
,对于每次循环迭代,变量都具有作用域并具有共享副本。
#22楼
正如刚才提到的:
区别在于范围。
var
的作用域是最近的功能块,而let
的作用域是最近的封闭块 ,它可以小于功能块。 如果在任何块之外,两者都是全局的。让我们看一个例子:
范例1:
在我的两个示例中,我都有一个函数myfunc
。 myfunc
包含变量myvar
等于10。在第一个示例中,我检查myvar
等于10( myvar==10
)。 如果是,我会使用var
关键字声明一个变量myvar
(现在有两个myvar变量),并为其分配一个新值(20)。 在下一行中,我在控制台上打印其值。 在条件块之后,我再次在控制台上打印myvar
的值。 如果查看myfunc
的输出,则myvar
值等于20。
示例2 :在第二个示例中,我没有在条件块中使用var
关键字, myvar
使用let
关键字声明了myvar
。 现在,当我调用myfunc
,会得到两个不同的输出: myvar=20
和myvar=10
。
因此,区别非常简单,即其范围。
#23楼
功能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
被调用时,使用var
声明的变量foo仍然可以在if
语句之外访问。 在testVar
函数范围内的任何地方都可以使用此变量foo
。
let
变量:
当第二个函数testLet
被调用时,用let
声明的变量bar只能在if
语句内部访问。 因为用let
声明的变量是块范围的 (块是大括号之间的代码,例如if{}
, for{}
, function{}
)。
let
变量不被吊起:
var
和let
之间的另一个区别是,用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
over var
因为它的作用域更加具体。 这样可以减少在处理大量变量时可能发生的命名冲突。 当您希望将全局变量显式地放在window
对象上时,可以使用var
(如果确实需要,请务必仔细考虑)。
#24楼
我想将这些关键字链接到执行上下文,因为执行上下文在所有这些方面都很重要。 执行上下文具有两个阶段:创建阶段和执行阶段。 此外,每个执行上下文都具有可变环境和外部环境(其词法环境)。
在执行上下文的创建阶段,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并非如此。
#25楼
看一下这张图片,我创建了一个非常简单的示例来演示const
和let
变量。 如您所见,当您尝试更改const
变量时,您会收到错误消息( 尝试覆盖常量的'name' ),但请看一下let
变量...
首先,我们声明let age = 33
,然后分配其他值age = 34;
,没关系,尝试更改let
变量时没有任何错误
#26楼
当我目前正试图深入了解JavaScript时,我将分享我的简要研究成果,其中包括已经讨论过的一些很棒的文章,以及从不同角度来看的其他一些细节。
如果我们了解了函数和块作用域之间的区别,那么了解var和let之间的区别会更加容易。
让我们考虑以下情况:
(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 ,它将包含VariableEnvironment和与每个迭代相对应的所有LexicalEnvironments 。
还有一个更简单的例子
功能范围
function test() {
for(var z = 0; z < 69; z++) {
//todo
}
//z is visible outside the loop
}
区块范围
function test() {
for(let z = 0; z < 69; z++) {
//todo
}
//z is not defined :(
}
#27楼
让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.
#28楼
我认为术语和大多数示例有点让人不知所措。我个人与之不同的主要问题是了解什么是“阻止”。 在某种程度上,我意识到,一个块将是除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);
#29楼
下面是一些示例的let
关键字说明。
let
工作非常像var
。 主要区别在于var
变量的范围是整个封闭函数
Wikipedia上的此表显示哪些浏览器支持Javascript 1.7。
请注意,只有Mozilla和Chrome浏览器支持它。 IE,Safari和其他可能没有的。
#30楼
有一些细微的差异- let
范围界定的行为更像变量范围界定的行为,或多或少与任何其他语言一样。
例如,它的作用域为封闭的块,它们在声明之前不存在,等等。
但是,值得注意的是, let
只是较新的Javascript实现的一部分,并且具有不同程度的浏览器支持 。