6280

引入ECMAScript 6这个陈述.

我听说它被描述为局部变量,但我仍然不太确定它与无功功率,无功功率关键字。

有什么区别?什么时候应该被用来代替无功功率,无功功率?

0

38个答案38

重置为默认值
8228

范围界定规则

主要区别在于范围规则。声明的变量无功功率,无功功率关键字的作用域是直接函数体(因此是函数作用域),而变量的作用域为立即数封闭块表示为{ }(因此是块范围)。

函数run(){var foo=“foo”;设bar=“bar”;console.log(foo,bar);//Foo酒吧{var moo=“Mooo”设baz=“Bazz”;console.log(moo,baz);//穆奥巴兹}console.log(moo);//穆奥控制台.log(baz);//引用错误}运行();

为什么该语言引入了关键字,因为函数范围令人困惑,并且是JavaScript中错误的主要来源之一。

看看这个例子另一个堆栈溢出问题:

var函数=[];//让我们创建3个函数对于(var i=0;i<3;i++){//并将其存储在函数中funcs[i]=函数(){//每个都应该记录其值。console.log(“我的值:”+i);};}对于(var j=0;j<3;j++){//现在让我们逐一看看函数[j]();}

我的价值观:3每次都输出到控制台函数[j]();由于匿名函数绑定到同一变量,因此调用了。

人们必须创建立即调用的函数来从循环中捕获正确的值,但这也很麻烦。

吊装

用声明的变量无功功率,无功功率关键字是吊装和已初始化这意味着即使在声明它们之前,也可以在其封闭范围内访问它们,但它们的值是未定义在到达声明语句之前:

功能检查提升(){console.log(foo);//未定义var foo=“foo”;console.log(foo);//}检查吊装();

变量是已吊装,但未初始化直到他们的定义得到评估。在初始化之前访问它们会导致引用错误。该变量称为in时间死区从块的开始直到处理声明语句。

功能检查提升(){console.log(foo);//引用错误让foo=“foo”;console.log(foo);//}检查提升();

创建全局对象属性

在顶层,,不像无功功率,无功功率,不在全局对象上创建属性:

var foo=“foo”;//全局范围的让bar=“bar”;//全局范围,但不是全局对象的一部分console.log(window.foo);//console.log(window.bar);//未定义

重声明

在严格模式下,无功功率,无功功率将允许您在同一范围内重新声明同一变量,同时引发语法错误。

'使用严格';var foo=“foo1”;var foo=“foo2”;//没问题,“foo1”被替换为“foo2”。设bar=“bar1”;设bar=“bar2”;//语法错误:已声明标识符“bar”

15
  • 76
    记住,您可以随时创建块。function(){code;{let inBlock=5;}代码;};
    – 普通乔
    评论 2012年12月14日10:14
  • 259
    那么,let语句的目的只是在某个块中不需要时释放内存吗? 评论 2013年6月7日5:18
  • 302
    @没有错误,是的,我们鼓励变量只存在于需要的地方。
    – 蝙蝠侠
    评论 2013年6月7日15:02
  • 75
    块表达式let(变量声明)语句是非标准的,将来将被删除,bugzilla.mozilla.org/show_bug.cgi?编号=1023609.
    – 加尤斯
    评论 2014年12月17日14:51
  • 11
    尽管let封闭块已弃用,但您也可以通过创建带大括号的显式块来完成相同的操作。{让bar=foo;让foo=bar;代码;更多代码; console.log(bar+foo);}只需将代码块顶部的let用大括号括起来。 评论 2015年5月7日14:12
831

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

对于(var i=1;i<6;i++){$(“#div”+i)点击(函数(){console.log(i);});}
<script src=“https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js“></script><p>单击每个数字将登录到控制台:</p><div id=“div1”>1<div id=“div2”>2<div id=“div3”>3<div id=“div4”>4<div id=“div5”>5

上面的代码演示了一个典型的JavaScript关闭问题。参考变量存储在click处理程序闭包中,而不是.

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

一个通用的解决方法是将其封装在一个匿名函数中并传递作为一个论点。现在也可以通过使用相反无功功率,无功功率如下面的代码所示。

(在Chrome和Firefox 50中测试)

for(设i=1;i<6;i++){$(“#div”+i)点击(函数(){console.log(i);});}
<script src=“https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js“></script><p>单击每个数字将登录到控制台:</p><div id=“div1”>1<div id=“div2”>2<div id=“div3”>3</div><div id=“div4”>4<div id=“div5”>5

14
  • 80
    这真的很酷。我希望在括号内的循环体包含的外部定义“I”,而不是在“I”周围形成“闭包”。当然,你的例子证明了另一种情况。我认为从语法的角度来看,这有点令人困惑,但这种情况非常常见,因此以这种方式支持它是有意义的。非常感谢您提出这个问题。 评论 2015年7月27日12:49
  • 12
    IE 11支持,但它会对所有按钮发出“6”警报。你有消息说应该表现得好吗? 评论 2015年10月22日13:29
  • 13
    看起来你的回答是正确的行为:developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… 评论 2015年10月22日13:32
  • 17
    实际上,这是Javascript中的一个常见陷阱,现在我明白了为什么会非常有用。在循环中设置事件侦听器不再需要立即调用函数表达式来进行本地作用域每次迭代时。 评论 2016年2月21日8:12
  • 26
    使用“let”只是推迟了这个问题。因此,每次迭代都会创建一个独立的私有块范围,但“i”变量仍然会被块内的后续更改损坏(假定迭代器变量不是通常在块内更改,但块内其他声明的let变量可能会更改),并且块内声明的任何函数在调用时都会损坏块内声明其他函数的“i”值,因为它们共享相同的私有块范围,因此对“i”的引用相同。
    – 加里
    评论 2016年9月7日23:10
334

两者的区别是什么无功功率,无功功率?

  • 使用无功功率,无功功率语句自始至终都是众所周知的函数它是从函数开始在中定义的。(*)
  • 使用语句仅在中已知街区从定义开始,它就在中定义。(**)

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

//我在这里不认识//这里不知道j//k在此已知,但未定义//我在这里不认识功能循环(arr){//我在这里是已知的,但没有定义//此处不知道j//k在这里是已知的,但只有一个值,称为第二个时间循环//我在这里不认识对于(var i=0;i<arr.length;i++){//我在这里是已知的,并且有一个值//此处不知道j//k在这里是已知的,但有一个值,只有第二个时间循环被调用//我在这里不认识};//我在这里是已知的,并且有一个值//此处不知道j//k在这里是已知的,但只有一个值,称为第二个时间循环//我在这里不认识for(设j=0;j<arr.length;j++){//我在这里是已知的,并且有一个值//j在这里是已知的,并且有一个值//k在这里是已知的,但有一个值,只有第二个时间循环被调用//我在这里不认识};//我在这里是已知的,并且有一个值//此处不知道j//k在这里是已知的,但只有一个值,称为第二个时间循环//我在这里不认识}回路([1,2,3,4]);对于(var k=0;k<arr.length;k++){//我在这里不认识//此处不知道j//k在这里是已知的,并且有一个值//我在这里不认识};for(设l=0;l<arr.length;l++){//我在这里不认识//此处不知道j//k在这里是已知的,并且有一个值//l在这里是已知的,并且有一个值};回路([1,2,3,4]);//我在这里不知名//此处不知道j//k在这里是已知的,并且有一个值//我在这里不认识

在这里,我们可以看到我们的变量j个只在第一个for循环中知道,但在之前和之后都不知道。然而,我们的变量在整个函数中是已知的。

此外,考虑到块范围的变量在声明之前是未知的,因为它们没有被提升。也不允许在同一块中重新声明同一块范围内的变量。这使得块范围的变量比全局或函数范围的变量更不容易出错,这些变量被提升,并且在多个声明的情况下不会产生任何错误。


使用安全吗今天?

有些人会认为,将来我们将只使用let语句,而var语句将过时。JavaScript专家凯尔·辛普森写的一篇非常详尽的文章,阐述了为什么他认为情况不会如此.

然而,今天的情况绝对不是这样。实际上,我们需要问问自己使用声明。这个问题的答案取决于您的环境:

  • 如果您正在编写服务器端JavaScript代码(节点.js),您可以安全地使用声明。

  • 如果您正在编写客户端JavaScript代码并使用基于浏览器的transpiler(例如练习者babel-standalone公司),您可以安全地使用语句,但您的代码在性能方面可能不是最佳的。

  • 如果您正在编写客户端JavaScript代码并使用基于节点的transpiler(如traceur shell脚本巴别塔),您可以安全地使用声明。而且,由于您的浏览器只知道已传输的代码,因此性能缺陷应该受到限制。

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

    仍有一些浏览器不支持完全:

在此处输入图像描述


如何跟踪浏览器支持

有关哪些浏览器支持阅读此答案时的声明,请参阅我能用吗第页.


(*)全局和功能范围内的变量可以在声明之前进行初始化和使用,因为JavaScript变量悬挂的.这意味着声明总是移动到范围的顶部。

(**)块作用域变量未提升

6
  • 23
    关于答案v4:在功能块中处处可见!它开始于未定义(由于吊装),直到您指定一个值!聚苯乙烯:也被提升(至其承载块的顶部),但会产生引用错误当在第一次分配之前在块中被引用时。(附言2:我是一个支持分号的人,但你真的不需要在块后加分号)。话虽如此,感谢您添加有关支持的真实性检查! 评论 2016年5月21日4:41
  • @GitaarLAB:根据Mozilla开发者网络:“在ECMAScript 2015中,let绑定不受Variable Hoisting的约束,这意味着let声明不会移动到当前执行上下文的顶部。”-总之,我对我的回答做了一些改进,这应该会澄清两个变量之间提升行为的差异无功功率,无功功率! 评论 2018年2月26日23:37
  • 2
    你的答案进步了很多(我仔细检查了一下)。请注意,您在评论中引用的同一链接也表示:“(let)变量位于块的开头直到处理完初始化。“这意味着‘标识符’(文本字符串‘reserved’指向‘something’)已经是了保留在相关作用域中,否则它将成为root/host/window作用域的一部分。就我个人而言,“吊装”只意味着将声明的“标识符”保留/链接到其相关范围;排除其初始化/分配/可修改性! 评论 2018年3月1日18:16
  • 还有+1.你链接的凯尔·辛普森文章是杰出的阅读,谢谢你!“时间死区”也很清楚,即“TDZ”。我想补充一件有趣的事:我在MDN上读到常数建议仅在您实际需要其附加功能时使用,因为强制/检查这些额外功能(如只写常量)会导致(当前)引擎执行/检查/验证/设置的“更多工作”(以及作用域树中的其他作用域节点)。 评论 2018年3月1日18:17
  • 2
    请注意,MDN表示IE确实正确地解释了let。是哪一个?developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… 评论 2019年2月6日12:42
186

这里有一个解释关键字用一些例子。

工作原理非常类似无功功率,无功功率主要区别在于无功功率,无功功率变量是整个封闭函数

这张桌子维基百科上显示了哪些浏览器支持Javascript 1.7。

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

5
  • 7
    链接文档中的关键文本位似乎是“let的工作方式与var非常相似。主要区别在于var变量的范围是整个封闭函数”。 评论 2009年4月17日20:25
  • 59
    @奥利耶,实际上Mozilla正处于领先地位。参见第19页,共页ecma-international.org/publications/files/ecma-ST/ecma-262.pdf 评论 2012年6月18日20:16
  • 1
    @泰勒·克朗普顿(Tyler Crompton),这只是多年来保留下来的一组单词。当mozilla添加let时,它纯粹是一个mozila扩展,没有相关规范。ES6应该定义let语句的行为,但这是在mozila引入语法之后发生的。记住moz也有E4X,它完全死了,只有moz。
    – 奥利耶
    评论 2012年7月11日18:49
  • 11
    IE11增加了对的支持 msdn.microsoft.com/en-us/library/ie/dn342892%28v=vs.85%29.aspx
    – 私奔
    评论 2013年12月24日12:59
  • 2
    现在目前支持除Opera、黑莓和QQ浏览器以外的所有最新浏览器。 评论 2019年1月8日5:11
153

块范围

使用关键字是块范围的,这意味着它们仅在他们在其中被宣布。

在顶层(函数外部)

在顶层,使用不要在全局对象上创建属性。

var全局变量=42;让blockScopedVariable=43;console.log(globalVariable);//42console.log(blockScopedVariable);//43console.log(this.globalVariable);//42console.log(this.blockScopedVariable);//未定义

函数内部

在函数内部(但在块外部),具有与相同的范围无功功率,无功功率.

(() => {var函数ScopedVariable=42;让blockScopedVariable=43;console.log(functionScopedVariable);//42console.log(blockScopedVariable);//43})();console.log(functionScopedVariable);//ReferenceError:未定义functionScopedVariableconsole.log(blockScopedVariable);//ReferenceError:未定义blockScopedVariable

在块内

使用声明的变量块内部不能在该块外部访问。

{var全局变量=42;让blockScopedVariable=43;console.log(globalVariable);//42console.log(blockScopedVariable);//43}console.log(globalVariable);//42console.log(blockScopedVariable);//ReferenceError:未定义blockScopedVariable

在循环内部

用声明的变量in循环只能在该循环内引用。

对于(var i=0;i<3;i++){var j=i*2;}控制台.log(i);//控制台.log(j);//4for(设k=0;k<3;k++){设l=k*2;}console.log(k类型);//未定义console.log(l类型);//未定义//尝试在此处执行console.log(k)或console.log(l)操作会引发ReferenceError。

带闭包的循环

如果您使用而不是无功功率,无功功率在循环中,每次迭代都会得到一个新变量。这意味着您可以在循环中安全地使用闭包。

//记录三次,不是我们的意思。对于(var i=0;i<3;i++){setTimeout(()=>console.log(i),0);}//按预期记录0、1和2。for(设j=0;j<3;j++){setTimeout(()=>console.log(j),0);}

时间死区

因为时间死区,变量声明使用在声明之前无法访问。尝试这样做会引发错误。

控制台.log(noTDZ);//未定义var noTDZ=43;console.log(hasTDZ);//ReferenceError:未定义hasTDZ设hasTDZ=42;

无需重新申报

不能使用多次声明同一变量。也不能使用声明变量与使用声明的另一个变量具有相同的标识符无功功率,无功功率.

变量a;变量a;//效果很好。让b;让b;//语法错误:已声明标识符“b”变量c;让c;//语法错误:标识符“c”已声明

常数

常数非常类似于-它是块状的,有TDZ。然而,有两件事是不同的。

无重新签名

使用声明的变量常数无法重新分配。

常数a=42;a=43;//TypeError:赋值给常量变量。

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

常量对象={};对象a=42;控制台.log(obj.a);//42

如果您想拥有一个不可变对象,应该使用Object.freeze().

const obj=Object.freeze({a:40});对象a=42;控制台.log(obj.a);//40控制台.log(obj.b);//未定义

需要初始值设定项

在使用声明变量时,始终必须指定值常数.

常数a;//语法错误:常量声明中缺少初始值设定项
0
145

接受的答案缺少一点:

{设a=123;};控制台.log(a);//ReferenceError:未定义a
4
  • 21
    公认的答案并没有在其示例中解释这一点。接受的答案仅在对于循环初始值设定项,大大缩小了。投了赞成票。 评论 2015年9月22日6:55
  • 45
    @stimpy77它显式地声明“让范围限定到最近的封闭块”;是否需要包括所有显示方式? 评论 2016年3月31日21:32
  • 9
    有很多例子,但没有一个能恰当地证明这一点。。我可能会投票支持接受的答案和这个答案? 评论 2016年3月31日21:38
  • 6
    这一贡献表明,“块”可以只是一组括在括号中的线;也就是说,它不需要与任何类型的控制流、回路等相关联。
    – 韦韦洛
    评论 2017年11月22日14:37
112

在最基本的术语中,

for(设i=0;i<5;i++){//我可以接近✔️}//我不方便

对于(var i=0;i<5;i++){//我可以接近✔️}//我可以接近✔️

⚡️ 沙盒四处游玩↓

编辑let vs var

0
71

以下是这两者之间的区别示例:
在此处输入图像描述

正如你所见变量j变量的值仍在for循环范围(块范围)之外,但让我来吧在for循环范围外未定义变量。

“使用严格”;console.log(“变量:”);对于(var j=0;j<2;j++){console.log(j);}控制台日志(j);console.log(“let:”);for(设i=0;i<2;i++){控制台.log(i);}控制台.log(i);

0
70

主要区别是范围差异,而只能在范围它是声明的,就像在for循环中一样,无功功率,无功功率例如,可以在循环外部访问。从中的文档MDN公司(同样来自MDN的示例):

允许您声明在其上使用的块、语句或表达式的范围内受限的变量。这与无功功率,无功功率关键字,它全局定义变量,或局部定义整个函数的变量,而不管块范围如何。

声明的变量将定义它们的块以及任何包含的子块作为其范围。这样,工作原理非常类似无功功率,无功功率主要区别在于无功功率,无功功率变量是整个封闭函数:

函数varTest(){变量x=1;if(真){var x=2;//相同的变量!控制台.log(x);//2}控制台.log(x);//2}函数letTest(){设x=1;if(真){设x=2;//不同的变量console.log(x);//2}控制台.log(x);//1}`

在程序和功能的顶层,,不像无功功率,无功功率,不在全局对象上创建属性。例如:

var x=“全局”;设y=“全局”;console.log(this.x);//“全球”console.log(this.y);//未定义

当在块内使用时,let将变量的作用域限制在该块内。请注意以下各项之间的差异无功功率,无功功率其范围在声明它的函数内。

变量a=1;var b=2;如果(a===1){变量a=11;//范围是全球性的设b=22;//作用域在if-block中控制台.log(a);//11控制台.log(b);//22} 控制台.log(a);//11控制台.log(b);//2

另外,不要忘记它的ECMA6功能,所以它还没有完全支持,所以最好总是使用Babel等将其传输到ECMA5…有关访问的更多信息巴贝尔网站

1
  • 我不知道最后一个例子是否准确。因为不是从函数调用它,而是从直接命令行调用它,它仍然被视为同一函数的一部分。因此,如果从函数外部调用它,它的行为不应该相同。 评论 2020年8月28日18:20
66

有一些细微的差异-作用域的行为与其他语言中的变量作用域差不多。

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

然而值得注意的是只是较新的Javascript实现的一部分,具有不同程度的浏览器支持.

5
  • 14
    同样值得注意的是,ECMAScript是标准的包含在第6版草稿最有可能出现在最终规范中。 评论 2012年3月31日15:09
  • 8
    只是对这个问题感到困惑,2012年仍然只有Mozilla浏览器支持Safari、IE和Chome都没有。
    – 伪学者
    评论 2012年7月13日17:38
  • 4
    意外地在事故中创建部分阻塞范围的想法是一个好主意,小心,不提升,以使用由定义在块的顶部。如果您有如果语句,您可能忘记了在定义变量之前不能使用该变量。很棒!!! 评论 2015年5月7日14:01
  • 1
    这是let和var之间最重要的区别之一,这不在公认的答案中哈哈。特别是考虑到由于吊装和范围界定可能出现的众多缺陷。如果你不提到吊装,我觉得let和var之间没有太多区别。
    – 杰伊
    评论 2015年6月21日16:12
  • 4
    @EricB:是和否:“在ECMAScript 2015中, 将提升变量设置为块的顶部。但是,在变量声明之前引用块中的变量会导致引用错误(我的笔记:而不是老好人未定义). 从块的开始到声明被处理,变量处于“暂时死区”。“switch语句也是如此,因为只有一个底层块”。来源:developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… 评论 2016年5月21日4:15
35
  • 变量未提升

    非起重机它们出现在块的整个范围内。相比之下,无功功率,无功功率可按如下方式起吊。

    {console.log(抄送);//未定义。吊装引起的变异系数cc=23;}{控制台.log(bb);//ReferenceError:bb未定义设bb=23;}

    实际上,佩尔·贝尔吉,两者都有无功功率,无功功率已吊装.

  • 垃圾收集

    块范围与闭包和垃圾收集有关以回收内存非常有用。考虑一下,

    函数过程(数据){//...}var hugeData={..};流程(hugeData);var btn=文档.getElementById(“mybutton”);btn.addEventListener(“点击”,功能点击(evt){//....});

    这个点击处理程序回调不需要庞大的数据完全可变。理论上,之后进程(…)运行,巨大的数据结构庞大的数据可能会被垃圾收集。然而,一些JS引擎可能仍然需要保持这种巨大的结构,因为点击函数在整个范围内有一个闭包。

    然而,块范围可以使这个巨大的数据结构被垃圾收集。

    功能过程(数据){//...}{//此块内声明的任何内容都可以进行垃圾收集让hugeData={..};流程(hugeData);}var btn=文档.getElementById(“mybutton”);btn.addEventListener(“点击”,功能点击(evt){//....});
  • 循环

    循环中可以重新绑定对于循环的每个迭代,请确保重新为其分配上一次循环迭代结束时的值。考虑一下,

    //打印5次对于(var i=0;i<5;++i){setTimeout(函数(){控制台.log(i);}, 1000);  }

    但是,请更换无功功率,无功功率具有

    //打印1、2、3、4、5。现在for(设i=0;i<5;++i){setTimeout(函数(){控制台.log(i);}, 1000);  }

    因为创建一个新的词汇环境,其中包含a)initialiser表达式b)每个迭代(在前面计算增量表达式)的名称,更多详细信息如下在这里.

1
  • 6
    是的,他们被吊起了,但表现得好像没有被吊起一样,因为(鼓滚)暂时死区——一个非常戏剧性的名字,一个标识符在声明之前是无法访问的:-)
    – 德雷尼
    评论 2016年12月31日15:42
35

区别在于范围每个变量声明的变量。

实际上,范围上的差异有许多有益的后果:

  1. 变量仅在其最近的封闭块({ ... }).
  2. 变量只能在出现的代码行中使用之后声明变量(即使它们被吊起了!).
  3. 变量不能由后续无功功率,无功功率.
  4. 全球的变量未添加到全局窗口对象。
  5. 变量是易于使用带闭包(它们不会导致竟态条件).

由实施的限制减少变量的可见性,增加早期发现意外名称冲突的可能性。这样可以更容易地跟踪和推理变量,包括可达性(帮助回收未使用的内存)。

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

无功功率,无功功率如果您确信在循环中使用闭包(5)或在代码中声明外部可见的全局变量(4)时需要单绑定效果,那么这可能仍然很有用。的使用无功功率,无功功率如果出口从transpiler空间迁移到核心语言。

示例

1.在最近的封闭块外不得使用:此代码块将引发引用错误,因为x个出现在用声明的块之外:

{设x=1;}console.log(`x是${x}`);//解析期间ReferenceError:“x未定义”。

相比之下,与无功功率,无功功率作品。

2.申报前不得使用:
此代码块将引发引用错误在代码可以运行之前,因为x个在声明之前使用:

{x=x+1;//解析期间ReferenceError:“x未定义”。设x;console.log(`x是${x}`);//从不跑步。}

相比之下,与无功功率,无功功率解析和运行时不会引发任何异常。

3.无再次申报:以下代码演示了用声明的变量以后不能重新声明:

设x=1;设x=2;//语法错误:已声明标识符“x”

4.未附加到的全局窗口:

var button=“我的名字太普通了,所以我会引发事故。”;let link=“虽然我的名字很常见,但我很难从其他JS文件访问。”;console.log(链接);//好 啊console.log(window.link);//未定义(好!)console.log(window.button);//好 啊

5.易于使用,带封口:用声明的变量无功功率,无功功率循环内部的闭包不能很好地工作。下面是一个简单的循环,它输出变量在不同的时间点具有:

for(设i=0;i<5;i++){console.log(`i是${i}`),125/*ms*/);}

具体而言,该输出:

i为0我是1我是2岁我是3岁我是4岁

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

for(设i=0;i<5;i++){setTimeout(_=>console.log(`i是${i}`),125/*ms*/);}

…只要我们坚持下去,产量保持不变相反,如果我们使用变量i而是:

对于(var i=0;i<5;i++){setTimeout(_=>console.log(`i是${i}`),125/*ms*/);}

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

我5岁我5岁我5岁我5岁我5岁
2
  • 11
    #5不是由比赛条件引起的。通过使用无功功率,无功功率而不是,代码相当于:var i=0;while(i<5){doSomethingLater();i++;} 不在关闭范围内,到那时稍后做某事()执行,已经增加了5倍,因此输出为我5岁五次。通过使用,变量位于闭包中,因此每个异步调用都会获得其自己的副本而不是使用使用创建的“全局”无功功率,无功功率. 评论 2017年6月2日1:12
  • @丹尼尔:我认为从循环初始值设定项中提升变量定义的转换并不能解释任何事情。这只是语义的正常定义对于。虽然更复杂,但更精确的转换是经典的for(var i=0;i<5;i++){(函数(j){setTimeout(_=>console.log(我是${j}),125/*毫秒*/);})(i) ;}它引入了一个“功能激活记录”来保存名称为j个函数内部。
    – 肉豆蔻
    评论 2017年7月25日7:13
27

这里有一个例子来补充其他人已经写过的东西。假设您想创建一个函数数组,加法器函数,其中每个函数都接受一个Number参数,并返回参数和数组中函数索引的总和。正在尝试生成加法器函数使用循环无功功率,无功功率关键字不会像人们天真地期望的那样起作用:

//一组加法器函数。var加法器函数=[];对于(var i=0;i<1000;i++){//我们希望索引i处的函数将索引添加到其参数中。加法器函数[i]=函数(x){//我在这里要做什么?返回x+i;};}var add12=加法器函数[12];//哦哦。该函数被绑定到外部作用域中的i,当前为1000。console.log(添加12(8)===20);//=>控制台.log(add12(8)===1008);//=>真的console.log(i);//=>1000//情况变得更糟了。i=-8;控制台.log(add12(8)===0);//=>真的

上述过程没有生成所需的函数数组,因为的范围超出了对于创建每个函数的块。相反,在循环的末尾在每个函数的闭包中指中每个匿名函数在循环末尾的值(1000)加法器函数。这根本不是我们想要的:我们现在在内存中有一个由1000个不同函数组成的数组,具有完全相同的行为。如果我们随后更新,突变将影响所有加法器函数.

但是,我们可以使用关键词:

//让我们再试一次。//注意:我们正在使用另一个ES6关键字const来表示不需要的值//被重新分配。const和let具有类似的作用域行为。常量加法器函数=[];for(设i=0;i<1000;i++){//注意:这次我们使用的是更新的箭头函数语法,但是//使用上一示例中的“function(x){…”语法//这里不会更改显示的行为。加法器函数[i]=x=>x+i;}const add12=加法器函数[12];//耶!行为符合预期。console.log(添加12(8)===20);//=>真的//我的范围没有扩展到for循环之外。console.log(i);//=>ReferenceError:未定义i

这次,是每个迭代的反弹对于循环。现在每个函数都保留在创建函数时,以及加法器函数行为符合预期。

现在,想象一下这两种行为的混合,你可能会明白为什么不建议混合新的行为常数和老人在一起无功功率,无功功率在同一脚本中。这样做可能会导致一些非常混乱的代码。

const doubleAdderFunctions=[];对于(var i=0;i<1000;i++){常数j=i;双加法器函数[i]=x=>x+i+j;}const add18=双加法器函数[9];const add24=双加法器函数[12];//调试这样的情况并不有趣,尤其是当//代码比这个示例中的代码更复杂。控制台.log(add18(24)===42);//=>控制台.log(add24(18)===42);//=>控制台.log(add18(24)===add24(18));//=>console.log(添加18(24)===2018);//=>控制台.log(add24(18)===2018);//=>控制台.log(add18(24)===1033);//=>真的控制台.log(add24(18)===1030);//=>真的

不要让这种事发生在你身上。使用门楣。

注:这是一个教学示例,旨在演示无功功率,无功功率/循环和函数闭包中的行为也很容易理解。这将是一种可怕的数字相加方式。但是,在其他环境中的现实世界中可能会遇到在匿名函数闭包中捕获数据的通用技术。基督教青年会。

  • 2
    @aborz:在第二个示例中,匿名函数语法也很酷。这正是我在C#中所习惯的。我今天学到了一些东西。
    – 巴顿
    评论 2015年2月20日8:59
  • 更正:技术上,此处描述的箭头函数语法=>developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
    – 巴顿
    评论 2015年3月16日6:58
  • 实际上,你不需要设值=i;. The对于语句创建词法块。
    – 牙刷
    评论 2015年10月22日22:38
27

解释摘自我在中等:

提升是一种JavaScript机制,其中包含变量和函数声明被解析器移动到其作用域的顶部实际的代码执行由JavaScript解释器开始。所以,实际上变量或函数在哪里声明并不重要,它们将无论其范围是否为全局或本地。这意味着

控制台日志(hi);var hi=“说hi”;

实际上被解释为

var hi=未定义;控制台日志(hi);hi=“打招呼”;

所以,正如我们刚才看到的,无功功率,无功功率变量正在被提升到顶部它们的作用域,并且正在使用未定义的值进行初始化这意味着我们可以在实际操作之前分配它们的值在代码中这样声明:

hi=“打个招呼”console.log(hi);//打个招呼变量hi;

关于函数声明,我们可以在像这样声明它们之前调用它们:

sayHi();//您好!函数sayHi(){console.log('Hi');};

另一方面,函数表达式没有被提升,因此我们将得到以下错误:

sayHi()//输出:“TypeError:sayHi不是函数var sayHi=函数(){console.log('Hi');};

ES6引入了JavaScript开发人员常数关键字。While期间常数是块范围的,不起作用范围界定为无功功率,无功功率在讨论他们的起重行为。我们将从末尾开始,JavaScript提升常数.

console.log(hi);//输出:初始化前无法访问“hi”让hi=“hi”;

正如我们在上面看到的,不允许我们使用未申报的变量,因此解释器显式输出引用错误表明你好之前无法访问变量初始化。如果我们更改以上内容,也会出现相同的错误常数

console.log(hi);//输出:初始化前无法访问“hi”const hi=“嗨”;

因此,最重要的是,JavaScript解析器搜索变量声明和函数,并将它们提升到其范围的顶部在代码执行之前,在内存中为它们赋值如果解释器在执行代码时遇到他们将识别它们,并能够使用它们执行代码赋值。用声明的变量常数保持在变量执行开始时未初始化用声明无功功率,无功功率正在使用值初始化未定义.

我添加了这个视觉插图,以帮助更好地理解如何吊装变量和函数保存在内存中输入图像此处显示描述

1
  • 在堆栈溢出上需要对引用的内容进行适当的属性。这包括明确披露从属关系,并在从其他位置复制内容时清晰显示。。。即使你是作者。
    – 泰勒H
    评论 2022年2月11日14:28
19

功能VS块范围:

两者之间的主要区别无功功率,无功功率是用声明的变量吗无功功率,无功功率函数作用域.where函数用声明块作用域。例如:

函数testVar(){if(真){var foo='foo';}console.log(foo);}testVar();//日志“foo”函数testLet(){if(真){设bar=“bar”;}console.log(bar);}testLet();//参考误差//bar的作用域是if语句的块

变量无功功率,无功功率:

当第一个函数测试变量调用变量foo,用声明无功功率,无功功率,仍然可以从外部访问如果声明。此变量foo公司将可用到处测试变量 功能.

变量:

当第二个函数testLet测试被称为变量bar,用声明,只能在如果声明。因为变量是用声明的块作用域(其中块是花括号之间的代码,例如如果{},用于{},函数{}).

变量无法提升:

之间的另一个区别无功功率,无功功率是用声明的变量 不要被吊起来。示例是说明此行为的最佳方式:

变量 不要获得提升:

console.log(letVar);设letVar=10;//referenceError,变量未被提升

变量无功功率,无功功率 吊装:

控制台.log(varVar);var变量=10;//日志未定义,变量被提升

全球的无法连接到窗口:

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

var bar=5;设foo=10;console.log(bar);//日志5console.log(foo);//日志10console.log(window.bar);//日志5,添加到窗口对象的变量console.log(window.foo);//日志未定义,变量未添加到窗口对象


什么时候应该被过度使用无功功率,无功功率?

使用结束无功功率,无功功率只要你能,因为它的范围更明确。这减少了处理大量变量时可能发生的潜在命名冲突。无功功率,无功功率当您希望全局变量显式位于窗口对象(如果确实需要,请始终仔细考虑)。

1
  • "变量不会被提升:"变量是用let还是const来声明的?- 声明是被吊起的,而不是初始化这就是为什么时间死区存在-因为运行时可以在到达声明之前了解它。因此知道这是一个错误,因为在声明变量之前引用了它。因为声明被取消了。 评论 2023年9月18日12:15
19

ES6引入了两个新关键字(常数)替换为无功功率,无功功率.

当您需要块级减速时,可以使用let和const而不是var。

下表总结了var、let和const之间的差异

在此处输入图像描述

2
  • 11
    吊装立柱不正确。他们都提升变量。与的区别无功功率,无功功率它们提升但不初始化为未定义值。如果它们不提升,则不会在封闭块中屏蔽同名的变量:stackoverflow.com/q/63337235/2326961 评论 2020年8月11日9:43
  • var x=2;不同于x=2;
    – a55型
    评论 5月29日2:33
17

很有趣,因为它允许我们这样做:

(() => {var计数=0;for(设i=0;i<2;++i){for(设i=0;i<2;++i){for(设i=0;i<2;++i){console.log(计数++);}}}})();
.as-console-wrapper{最大高度:100%!重要;}

这将导致计数[0,7]。

鉴于

(() => {var计数=0;对于(var i=0;i<2;++i){对于(var i=0;i<2;++i){对于(var i=0;i<2;++i){console.log(计数++);}}}})();

仅计数[0,1]。

2
  • 是的,这增加了比必要的更多的困惑,也增加了不应该有的困惑。 评论 2021年10月4日19:27
  • @Bekim Bacaj这是一个人为的示例,说明了let和var之间的区别。也就是说,在循环的末尾,let声明的变量超出了范围,而var仍然存在。由程序员根据自己的意图和先前的经验决定将哪些构造合并到代码中。这个例子的目的不是要引起混淆,而是给读者一个起点,让他们以创造性的方式使用let构造来熟悉它。 评论 2021年10月6日2:29
16

看起来,至少在Visual Studio 2015中,TypeScript 1.5“var”允许在一个块中多次声明相同的变量名,而“let”则不允许。

这不会生成编译错误:

var x=1;var x=2;

这将:

设x=1;设x=2;
15
var-->功能范围let-->块范围const-->块范围

无功功率,无功功率

在这个代码示例中,变量使用声明无功功率,无功功率。因此,它具有功能范围。这意味着您可以访问仅从内部函数x。您无法从外部读取函数x

函数x(){var i=100;控制台.log(i);//100}console.log(i);//错误。你不能这么做x();

在这个示例中,您可以看到在内部声明如果块。但它是用无功功率,无功功率因此,它获得了功能范围。这意味着您仍然可以访问变量里面函数x.因为无功功率,无功功率始终将范围限定为函数。即使变量在内部声明如果块,因为它正在使用无功功率,无功功率它的作用域是父级函数x.

函数x(){if(真){var i=100;}控制台.log(i);}x();

Now变量在中声明函数y因此,范围至函数y。您可以访问里面函数y.但不是从外面函数y.

函数x(){函数y(){var i=100;控制台.log(i);}y();}x();

函数x(){函数y(){var i=100;}console.log(i);//错误}x();

let,常量

let和const具有块作用域。

常数行为相同。但不同的是,当您为常数你不能重新设计。但您可以重新设计价值观.

在本例中,变量在内部声明如果块。所以只能从内部访问如果块。我们不能从外面进入如果块。(此处常数工作方式与)

if(真){设i=100;控制台.log(i);//输出:100}控制台.log(i);//错误

函数x(){if(真){设i=100;控制台.log(i);//输出:100}控制台.log(i);//错误}x();

另一个区别是(let,const)无功功率,无功功率你可以访问吗无功功率,无功功率在声明之前定义变量。它将为您提供未定义.但如果你这样做常数它会给你一个错误。

console.log(x);var x=100;

控制台.log(x);//错误设x=100;

11

无功功率,无功功率是全局范围(可提升)变量。

常数是块范围。

测试.js

{设l=“let”;const c=“const”;var v=“var”;v2='var 2';}console.log(v,this.v);控制台.log(v2,this.v2);控制台.log(l);//ReferenceError:未定义l控制台.log(c);//ReferenceError:c未定义

11

如果我当时读了说明书 谢天谢地也可以用来避免自调用函数用于模拟仅私有成员-一种流行的设计模式会降低代码的可读性,使调试复杂化,不会增加真正的代码保护或其他好处,除非能够满足某人对语义的需求,所以停止使用它。/rant

var SomeConstructor;{让privateScope={};SomeConstructor=函数SomeConstrator(){this.someProperty=“foo”;privateScope.hiddenProperty=“bar”;}SomeConstructor.prototype.showPublic=函数(){console.log(this.someProperty);//foo公司}SomeConstructor.prototype.showPrivate=函数(){console.log(privateScope.hiddenProperty);//酒吧}}var myInstance=新建SomeConstructor();myInstance.showPublic();myInstance.showPrivate();console.log(privateScope.hiddenProperty);//错误

请参阅“仿真专用接口'

2
  • 您能否详细说明立即调用函数表达式如何不提供“代码保护”和做?(我假设您指的是具有“自调用函数”的IIFE。) 评论 2020年3月1日3:58
  • 你为什么设置隐藏属性在构造函数中?只有一个隐藏属性对于“类”中的所有实例。 评论 2020年3月1日12:52
9

使用时

这个关键字将变量声明附加到任意块的范围(通常为{ .. }配对)。换句话说,隐式劫持任何块的变量声明范围。

无法在中访问变量窗口对象,因为无法全局访问它们。

函数a(){{//这是let变量的最大范围设x=12;}控制台.log(x);}a();//未捕获引用错误:未定义x

使用时无功功率,无功功率

无功功率,无功功率ES5中的变量在函数中具有范围,这意味着变量在函数内有效,而不是在函数本身之外有效。

无功功率,无功功率变量可以在窗口对象,因为无法全局访问它们。

函数a(){//这是var变量的最大范围{ var x=12;}控制台.log(x);}a();//12

如果你想了解更多,请继续阅读以下内容

关于范围的一个最著名的面试问题也可以满足无功功率,无功功率如下所示;

使用时

for(设i=0;i<10;i++){设置超时(函数a(){控制台.log(i)//打印0到9,这简直就是AWW!!!}, 100*i);}

这是因为在使用,对于每个循环迭代,变量都有作用域,并且有自己的副本。

使用时无功功率,无功功率

对于(var i=0;i<10;i++){设置超时(函数a(){控制台.log(i)//打印10乘以10}, 100*i);}

这是因为在使用无功功率,无功功率,对于每个循环迭代,变量都具有作用域并具有共享副本。

1
  • "无法在window对象中访问let变量,因为它们无法全局访问。“不,声明可以是全局的然而,它们不附加到全局对象. 评论 2023年9月18日12:06
7

一些黑客:

1

让统计数据=[16,170,10];let[年龄、身高、年级]=统计;console.log(高度)

2

设x=120,y=12;[x,y]=[y,x];控制台.log(`x:${x}y:${y}`);

三。

设节点={type:“标识符”,名称:“foo”};让{type,name,value}=节点;console.log(类型);//“标识符”console.log(名称);//“foo”console.log(值);//未定义设节点={type:“标识符”};让{type:localType,name:localName=“bar”}=节点;console.log(localType);//“标识符”console.log(本地名称);//“酒吧”

吸气剂和setter:

让jar={Cookie数量:10,获取厨师(){返回this.numberOfCookies;},设置cookie(值){this.numberOfCookies=值;}};console.log(jar.cookie)jar.cookies=7;console.log(jar.cookie)
4
  • 请问这是什么意思让{type,name,value}=节点;? 您创建了一个具有3个属性type/name/value的新对象,并用node中的属性值初始化它们吗? 评论 2017年6月15日7:55
  • 1
    在示例3中,您正在重新声明导致异常的节点。这些示例也适用于无功功率,无功功率也是。 评论 2019年1月9日10:57
  • 这并不能回答问题;它可以从解释每个代码块正在做什么中受益。
    – 泰勒H
    评论 2021年4月13日18:50
  • 1.、2.和3。不依赖。这是破坏性的。不是固有的声明。你可以使用无功功率,无功功率也。此外,这根本不是“黑客”,而是官方语言规范的一部分。jsbin.com/tibiquqohe/1/edit?js,控制台基本上与“吸气剂和setter:”部分,但它显示了对象初始化语法。同样,与.jsbin.com/qobelimamoda/1/edit?js,控制台这个答案根本没有解决这个问题。 评论 2023年9月18日12:04
6

下面显示了“let”和“var”在范围中的不同之处:

设gfoo=123;if(真){设gfoo=456;}控制台.log(gfoo);//123var hfoo=123;if(真){var hfoo=456;}console.log(hfoo);//456

这个gfoo公司,由定义最初是在全球范围,当我们宣布gfoo公司再次进入if子句它的范围已更改当一个新值被分配给该范围内的变量时,它不影响全球范围。

鉴于hfoo网络,由定义无功功率,无功功率最初位于全球范围,但当我们在if子句,它考虑了全局范围hfoo,尽管再次使用var来声明它。当我们重新设计其值时,我们看到全局范围hfuo也受到了影响。这是主要区别。

6

我刚遇到一个必须使用的用例无功功率,无功功率结束引入新的变量。这里有一个案例:

我想用动态变量名创建一个新变量。

let variableName=“a”;eval(“let”+variableName+'=10;');控制台.log(a);//这不起作用
var variableName='a';eval(“var”+variableName+'=10;');控制台.log(a);//这个管用

上面的代码不起作用,因为评估引入了一个新的代码块。声明使用无功功率,无功功率将在此代码块之外声明变量,因为无功功率,无功功率声明函数范围中的变量。

另一方面,在块范围内声明变量。所以,变量只在中可见评估块。

  • 2
    你什么时候必须创建一个动态变量名,然后才能访问它?创建对象并为其指定关键帧和值要好得多。
    – 用户14029620
    评论 2020年11月9日19:18
  • 事实上,这是因为JavaScript的重新声明 命题不允许。 评论 2021年10月4日17:46
  • 请参阅JavaScript中的“变量”变量并使用对象。 评论 2023年9月18日11:56
5

让vs var。这都是关于范围.

var变量是全局变量基本上到处都可以访问,而让变量不是全局的只有在右括号杀死它们时才存在。

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

var cat=“cat”;let dog=“dog”;var动物=()=>{var giraffe=“giraffe”;让lion=“lion”;console.log(cat)//将打印“cat”。console.log(dog)//将打印“dog”,因为dog是在此函数之外声明的(如varcat)。console.log(长颈鹿)//将打印“长颈鹿”。console.log(lion)//将打印“狮子”,因为狮子在范围内。}console.log(长颈鹿)//将打印“giraffe”,因为giraffes是一个全局变量(var)。console.log(lion)//将打印UNDEFINED,因为lion是一个“let”变量,现在已超出范围。

  • 1
    "var变量是全局变量,基本上可以在任何地方访问"这根本不是真的 无功功率,无功功率声明要么是全局的,要么包含在函数中。它们从来都不是唯一的全球性公司。console.log(长颈鹿)//将打印“长颈鹿”,因为长颈鹿是一个全局变量(var)。这也是完全错误的,而且从来都不是这样。 评论 2023年9月18日11:59
  • 运行代码会给出:错误:{“message”:“ReferenceError:giraffe is not defined”,“filename”:“stacksnippets.net/js“,”lineno“:26,”colno“:13} 评论 6月5日11:02
  • 1
    @克丽丝·霍尔正确。这就是我上面的评论。这个答案是完全错误和误导的。它所提供的代码应该是用来说明不存在的语言规则的。这是JS范围界定的工作原理。 评论 6月5日11:06

现在,我认为使用以下方法可以更好地将变量范围限定到语句块:

函数printnums(){//我在这里不方便for(设i=0;i<10;i+=){控制台.log(i);}//我在这里不方便//这里可以到达j对于(var j=0;j<10;j++){console.log(j);}//这里可以到达j}

我认为人们会在以后开始使用let,以便在JavaScript中使用类似于其他语言、Java、C#等的作用域。

人们对JavaScript中的作用域没有明确的理解,这是早先犯下的错误。

不支持使用.

通过这种方法,JavaScript中的错误将被删除。

请参阅ES6深入:let和const以便更好地理解它。

1

如上所述:

区别在于范围界定。无功功率,无功功率范围最接近功能作用域为最近的封闭块,其中可以小于功能块。如果在任何外部,两者都是全局的块。让我们看一个例子:

示例1:

在我的两个例子中,我都有一个函数我的函数.我的函数包含一个变量米瓦尔等于10。在我的第一个示例中,我检查了米瓦尔等于10(myvar==10) . 如果是,我可以声明一个变量米瓦尔(现在我有两个myvar变量)使用无功功率,无功功率关键字并为其分配一个新值(20)。在下一行中,我在控制台上打印它的值。在条件块之后,我再次打印米瓦尔在我的控制台上。如果您查看的输出我的函数,米瓦尔值等于20。

let关键字

示例2:在我的第二个示例中,而不是使用无功功率,无功功率我声明的条件块中的关键字米瓦尔使用关键字。现在我打电话的时候我的函数我得到两种不同的输出:myvar=20myvar=10.

因此,区别很简单,即其范围。

1
  • 4
    请不要发布代码图片,这在SO上被认为是一种不好的做法,因为未来的用户将无法搜索它(以及易访问性问题)。同样,这个答案也没有添加任何其他答案尚未解决的内容。
    – 肌病
    评论 2018年8月24日17:29

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

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

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

函数a(){b;让b;}a();>Uncaught ReferenceError:b未定义

对于用var定义的变量,如果引擎在当前执行上下文的变量环境中找不到该变量,那么它将向上进入范围链(外部环境)并检查外部环境的变量环境。如果在那里找不到它,它将继续搜索作用域链。let和const的情况并非如此。

let的第二个特点是它引入了块范围。块由花括号定义。示例包括功能块、if块、用于块等。当使用块内的let声明变量时,该变量仅在块内可用。事实上,每次运行块时,例如在for循环中,它都会在内存中创建一个新变量。

ES6还引入了用于声明变量的const关键字。const也是块范围的。let和const的区别在于,const变量需要使用初始值设定项声明,否则会生成错误。

最后,当涉及到执行上下文时,用var定义的变量将附加到“this”对象。在全局执行上下文中,这将是浏览器中的窗口对象。let或const的情况并非如此。

  • "用var定义的变量将被附加到this对象。“这是不正确的——它只有在全球范围内发生只有因为在全球范围内与全局对象相同。没有可连接的机制无功功率,无功功率声明否则。正好是两种不同机制的横截面。jsbin.com/yujezitafo/1/edit?js,控制台 评论 2023年9月18日11:45
  • "如果在那里找不到[var声明],它将继续搜索Scope Chain。let和const的情况并非如此。“这也是不真实的-仍将检查范围链常数声明jsbin.com/murobobiqe/1/edit?js,控制台 评论 2023年9月18日11:47
  • "let和const的区别在于,const变量需要使用初始值设定项声明,否则会生成错误。“这是一个反向解释。主要区别是常数 不允许重新分配.因此因为它不能重新分配,需要立即初始化。否则,未初始化的声明将是一个错误,正如您所预料的那样,因为您以后无法赋值。 评论 2023年9月18日11:51

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

了解两者之间的差异无功功率,无功功率如果我们能理解功能块式示波器.

让我们考虑以下情况:

(函数计时器(){对于(var i=0;i<=5;i++){setTimeout(函数notime(){console.log(i);},i*1000);}})();Stack VariableEnvironment//timer()的一个Variable环境;//当计时器超时时,每个迭代的值都是相同的5.[设置超时,i][i=5]4.[设置超时,i]3.[设置超时,i]2.[设置超时,i]1.[设置超时,i]0.[设置超时,i]####################    (函数计时器(){for(设i=0;i<=5;i++){setTimeout(函数notime(){console.log(i);},i*1000);}})();Stack LexicalEnvironment-每个迭代都有一个新的词汇环境5.[设置超时,i][i=5]词汇环境4.[设置超时,i][i=4]词汇环境3.[设置超时,i][i=3]词汇环境2.[设置超时,i][i=2]词汇环境1.[setTimeout,i][i=1]词汇环境0.[设置超时,i][i=0]

什么时候计时器()被称为执行上下文将同时包含可变环境以及所有词汇环境对应于每个迭代。

还有一个简单的例子

功能范围

功能测试(){对于(var z=0;z<69;z++){//托多}//z在循环外可见}

块范围

功能测试(){for(设z=0;z<69;z++){//待办事项}//z未定义:(}

简单地说,let和var的区别在于var是函数范围的,let是块范围的。

不是你想要的答案吗?浏览标记的其他问题问你自己的问题.