ECMAScript 6引入了the let statement . 我've heard it described as a 120236 variable, but I'仍然不太确定它与 var
关键字的行为有何不同 .
有什么区别?什么时候 let
应该在 var
上使用?
ECMAScript 6引入了the let statement . 我've heard it described as a 120236 variable, but I'仍然不太确定它与 var
关键字的行为有何不同 .
有什么区别?什么时候 let
应该在 var
上使用?
27 回答
以下是两者之间差异的示例(支持刚启动的chrome):
正如您所看到的,
var j
变量仍然具有for循环范围之外的值(块范围),但let i
变量在外部未定义for循环范围 .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" .
功能VS块范围:
var
和let
之间的主要区别在于var
声明的变量是 function scoped . 而使用let
声明的函数是 block scoped . 例如:variables with var:
当调用第一个函数
testVar
时,仍然可以在if
语句之外访问使用var
声明的变量foo . 此变量foo
在testVar
function 范围内可用 everywhere .variables with let:
当第二个函数
testLet
被调用时,使用let
声明的变量bar只能在if
语句中访问 . 因为用let
声明的变量是 block scoped (其中块是大括号之间的代码,例如if{}
,for{}
,function{}
) .让变量不被提升:
var
和let
之间的另一个区别是使用let
don't get hoisted 声明的变量 . 一个示例是说明此行为的最佳方式:let
don't 的变量被提升:var
do 变量被挂起:Global let没有附加到窗口:
在全局范围内使用
let
声明的变量(不是函数中的代码)不会作为属性添加到全局window
对象上 . 例如(此代码在全局范围内):尽可能使用
let
而不是var
,因为它的范围更加具体 . 这减少了在处理大量变量时可能发生的潜在命名冲突 . 当您希望显式地将全局变量放在window
对象上时,可以使用var
(如果确实需要,请务必仔细考虑) .let和var有什么区别?
使用
var
语句定义的变量在the function中是已知的,它是从函数的开头定义的 . (*)使用
let
语句定义的变量仅在the block中已知,从定义之后定义 . (**)要了解其中的差异,请考虑以下代码:
在这里,我们可以看到我们的变量
j
仅在第一个for循环中已知,但不在之前和之后 . 然而,我们的变量i
在整个函数中是已知的 .另外,请考虑块范围变量在声明之前是未知的,因为它们没有被提升 . 您也不允许在同一个块中重新声明相同的块范围变量 . 这使得块范围变量比全局或功能范围变量更不容易出错,这些变量被提升并且在多个声明的情况下不会产生任何错误 .
今天使用它是否安全?
有些人会争辩说,将来我们只会使用let语句,而var语句将会过时 . JavaScript大师Kyle Simpson写了a very elaborate article on why that's not the case .
然而,今天绝对不是这样 . 事实上,我们实际上需要问自己,使用
let
语句是否安全 . 这个问题的答案取决于您的环境:如果您正在编写服务器端JavaScript代码(Node.js),则可以安全地使用
let
语句 .如果您正在编写客户端JavaScript代码并使用转换器(如Traceur),则可以安全地使用
let
语句,但是您的代码在性能方面可能不是最优的 .如果您正在编写客户端JavaScript代码而不使用转换器,则需要考虑浏览器支持 .
今天,2018年6月8日,仍然有一些浏览器不支持
let
!如何跟踪浏览器支持
有关在阅读此答案时哪些浏览器支持
let
语句的最新概述,请参阅this Can I Use page .(*)全局和功能范围的变量可以在声明之前初始化和使用,因为JavaScript变量是hoisted . 这意味着声明始终位于范围的顶部 .
(**)未提升块范围变量
一些hacks
let
:1 .
2 .
3 .
Getter和setter with let:
这是一个带有一些例子的explanation of the let keyword .
维基百科上的This table显示哪些浏览器支持Javascript 1.7 .
请注意,只有Mozilla和Chrome浏览器支持它 . IE,Safari和其他人可能没有 .
让我们不要提升它们出现的整个范围 . 相比之下,var可以如下所示提升 .
实际上,Per @Bergi,Both var and let are hoisted .
let
的块范围与闭包和垃圾收集有关,以回收内存 . 考虑,click
处理程序回调根本不需要hugeData
变量 . 从理论上讲,在process(..)
运行之后,巨大的数据结构hugeData
可能会被垃圾收集 . 但是,有些JS引擎仍然需要保留这个庞大的结构,因为click
函数在整个范围内都有一个闭包 .但是,块范围可以使这个庞大的数据结构被垃圾收集 .
循环中的
let
可以 re-binds it 到循环的每次迭代,确保从前一循环迭代结束时重新赋值 . 考虑,但是,将
var
替换为let
因为
let
创建了一个新的词法环境,其中包含a)初始化表达式b)每次迭代(主要用于评估增量表达式)的名称,更多细节是here .可能以下两个函数显示了不同之处:
When Using let
let
关键字将变量声明附加到它所包含的任何块(通常是{ .. }
对)的范围内 . 换句话说,let
隐式劫持任何块的变量声明范围 .let
变量无法在window
对象中访问,因为它们无法全局访问 .When Using var
var
和ES5中的变量在函数中具有范围,这意味着变量在函数内有效,而不在函数本身之外 .var
变量可以在window
对象中访问,因为它们无法全局访问 .If you want to know more continue reading below
关于范围的最着名的面试问题之一也足以满足
let
和var
的确切用法,如下所示;When using let
这是因为当使用
let
时,对于每个循环迭代,变量都是作用域的并且有自己的副本 .When using var
这是因为当使用
var
时,对于每个循环迭代,变量都是作用域并具有共享副本 .let
很有意思,因为它允许我们做这样的事情:这导致计数[0,7] .
而
仅计数[0,1] .
正如刚才提到的:
Example1:
在我的两个例子中,我有一个函数
myfunc
.myfunc
包含一个变量myvar
等于10.在我的第一个例子中,我检查myvar
是否等于10(myvar==10
) . 如果是,我使用var
关键字声明变量myvar
(现在我有两个myvar变量)并为其赋值一个新值(20) . 在下一行中,我在我的控制台上打印它的值 . 在条件块之后,我再次在我的控制台上打印myvar
的值 . 如果查看myfunc
的输出,myvar
的值等于20 .Example2: 在我的第二个例子中,我没有在条件块中使用
var
关键字,而是使用let
关键字声明myvar
. 现在,当我调用myfunc
时,我得到两个不同的输出:myvar=20
和myvar=10
.所以区别很简单,即范围 .
让
块范围
使用
let
关键字声明的变量是块作用域的,这意味着它们仅在声明它们的block中可用 .在顶层(功能之外)
在顶层,使用
let
声明的变量不会在全局对象上创建属性 .在函数内部
在函数内部(但在块之外),
let
与var
具有相同的范围 .在一个街区内
在块内部使用
let
声明的变量无法在该块外部访问 .在循环中
在循环中使用
let
声明的变量只能在该循环内引用 .带闭包的循环
如果在循环中使用
let
而不是var
,则每次迭代都会得到一个新变量 . 这意味着您可以安全地在循环内使用闭包 .时间死区
由于the temporal dead zone,使用
let
声明的变量在声明之前无法访问 . 尝试这样做会引发错误 .没有重新申报
您不能使用
let
多次声明相同的变量 . 您也无法使用声明变量let
与使用var
声明的另一个变量具有相同的标识符 .const
const
非常类似于let
-it的块范围并且具有TDZ . 然而,有两件事是不同的 .无需重新分配
使用
const
声明的变量无法重新分配 .请注意,这并不意味着该值是不可变的 . 它的属性仍然可以改变 .
如果要拥有不可变对象,则应使用Object.freeze() .
初始化程序是必需的
使用
const
声明变量时,始终必须指定一个值 .var
是全局范围(可提升)变量 .let
和const
是块范围 .let
也可用于避免闭包问题 . 它绑定了新的 Value ,而不是保留旧的参考,如下面的例子所示 .DEMO
上面的代码演示了一个典型的JavaScript闭包问题对
i
变量的引用存储在单击处理程序闭包中,而不是i
的实际值 .每个单击处理程序都将引用同一个对象,因为只有一个计数器对象可以容纳6,因此每次单击时会得到6个 .
一般的解决方法是将它包装在一个匿名函数中并传递
i
作为参数 . 现在也可以使用let
而不是var
来避免此类问题,如下面的代码所示 .DEMO(在Chrome和Firefox 50中测试过)
本文明确定义了var,let和const之间的区别
https://medium.com/javascript-scene/javascript-es6-var-let-or-const-ba58b8dcde75#.esmkpbg9b
接受的答案缺少一点:
在MDN中查看此链接
区别在于范围界定 .
var
的范围限定为最近的功能块,let
的范围限定为最近的封闭块,该封闭块可以小于功能块 . 如果在任何区域之外,两者都是全球此外,使用
let
声明的变量在它们的封闭块中声明之前是不可访问的 . 如演示中所示,这将引发ReferenceError异常 .Demo:
全球:
在功能块之外使用它们非常相似 .
但是,使用
let
定义的全局变量不会作为属性添加到全局window
对象上,就像var
定义的那样 .功能:
在功能块中使用时它们是相同的 .
块:
这是区别 .
let
仅在for()
循环中可见,var
对整个函数可见 .重新声明:
假设严格模式,
var
将允许您在同一范围内重新声明相同的变量 . 另一方面,let
不会:如果我正确阅读规范,那么
let
thankfully 也可以用来避免用于模拟私有成员的自调用函数 - 一种流行的设计模式,它会降低代码的可读性,使调试变得复杂,不会增加真正的代码保护或其他好处 - 除非可能令人满意某人对语义的渴望,所以停止使用它 . /咆哮见'Emulating private interfaces'
这是一个添加其他人已经写过的例子 . 假设您要创建一个函数数组
adderFunctions
,其中每个函数都使用一个Number参数,并返回参数和函数索引在数组中的总和 . 尝试使用var
关键字循环生成adderFunctions
将无法像某人可能天真期望的那样工作:上面的过程不会生成所需的函数数组,因为
i
的范围超出了创建每个函数的for
块的迭代 . 相反,在循环结束时,每个函数的闭包中的i
指的是adderFunctions
中每个匿名函数的循环结束时的i
的值(1000) . 这根本不是我们想要的:我们现在在内存中有一个包含1000个不同函数的数组,具有完全相同的行为 . 如果我们随后更新i
的值,则突变将影响所有adderFunctions
.但是,我们可以使用
let
关键字再试一次:这次,
i
在for
循环的每次迭代中都会反弹 . 现在,每个函数在函数创建时保持i
的值,并且adderFunctions
表现得如预期 .现在,图像混合了两种行为,你不建议在同一个脚本中将较新的
let
和const
与较旧的var
混合使用 . 这样做会导致一些令人费解的混乱代码 .不要让这件事发生在你身上 . 使用短绒 .
以前在JavaScript中只有两个范围,即功能范围和全局范围 . 使用'
let
'关键字JavaScript现在已经引入了block-level
变量 .要全面了解'let'关键字,ES6: ‘let’ keyword to declare variable in JavaScript会有所帮助 .
看来,至少在Visual Studio 2015中,TypeScript 1.5,“var”允许在块中对同一个变量名进行多次声明,而“let”则不允许 .
这不会生成编译错误:
这将:
区别在于每个声明的变量的scope .
在实践中,范围差异有许多有用的后果:
let
变量仅在最近的封闭块({ ... }
)中可见 .let
变量仅在声明变量后出现的代码行中可用(即使they are hoisted!) .let
变量可能不会被后续var
或let
重新声明 .全局
let
变量未添加到全局window
对象 .let
变量易于使用闭包(它们不会导致race conditions) .let
施加的限制会降低变量的可见性,并增加早期发现意外名称冲突的可能性 . 这样可以更容易地跟踪和推理变量,包括它们的reachability(帮助回收未使用的内存) .因此,当在大型程序中使用或者以独立开发的框架以新的和意外的方式组合时,
let
变量不太可能导致问题 .如果您确定在循环中使用闭包(#5)或在代码中声明外部可见的全局变量(#4)时需要单绑定效果,则
var
可能仍然有用 . 如果export从转换器空间迁移到核心语言中,则可以取消使用var
进行导出 .例子
1. No use outside nearest enclosing block: 此代码块将引发引用错误,因为
x
的第二次使用发生在使用let
声明的块之外:相比之下,
var
的相同示例有效 .2. No use before declaration:
在代码可以运行之前,这段代码将抛出
ReferenceError
,因为在声明之前使用了x
:相比之下,
var
的相同示例在不抛出任何异常的情况下进行分析和运行 .3. No redeclaration: 以下代码演示了使用
let
声明的变量以后可能不会重新声明:4. Globals not attached to window:
5. Easy use with closures: 使用
var
声明的变量不适用于循环内的闭包 . 这是一个简单的循环,它输出变量i
在不同时间点具有的值序列:具体来说,这输出:
在JavaScript中,我们经常在比创建变量时更晚的时间使用变量 . 当我们通过传递给
setTimeout
的闭包延迟输出来证明这一点时:......只要我们坚持使用
let
,输出就会保持不变 . 相反,如果我们使用var i
代替:...循环意外地输出“我是5”五次:
现在我认为使用
let
对一个语句块有更好的变量范围:我认为人们将在这之后开始使用let,以便他们在JavaScript中具有类似的范围,如其他语言,Java,C#等 .
那些对JavaScript中的作用域不太了解的人过去常犯错误 .
使用
let
不支持提升 .使用此方法,JavaScript中出现的错误将被删除 .
请参阅ES6 In Depth: let and const以更好地理解它 .
有一些细微的差别 -
let
范围表现更像是变量范围,或多或少任何其他语言 .例如它适用于封闭块,它们在声明之前不存在,等等 .
然而值得注意的是
let
只是新Javascript实现的一部分,并且具有不同程度的browser support .主要区别在于 scope 差异,而 let 只能在其声明的范围内可用,例如在for循环中, var 可以在循环外部访问,例如 . 来自MDN中的文档(也来自MDN的示例):
也不要't forget it'的ECMA6功能,所以's not fully supported yet, so it'更好总是使用Babel等将其转发给ECMA5 ...有关访问babel website的更多信息
let是es6的一部分 . 这些功能将以简单的方式解释差异 .