358

我玩ES6已经有一段时间了,我注意到while变量用无功功率,无功功率按预期吊装。。。

console.log(名称类型);//未定义var name=“约翰”;

…用声明的变量常数吊装似乎有一些问题:

console.log(名称类型);//引用错误let name=“约翰”;

console.log(名称类型);//引用错误const name=“约翰”;

这是否意味着用常数没有吊起?这里到底发生了什么?两者之间有什么区别吗常数在这件事上?

0

8个答案8

重置为默认值
460

@第四种说法是正确的无法访问在申报之前。然而,这比那要复杂一些。

变量是否用声明常数没有吊装?这里到底发生了什么?

所有声明(无功功率,无功功率,,常数,功能,功能*,)被“吊起”在JavaScript中。这意味着,如果在范围中声明名称,则在该范围中,标识符将始终引用该特定变量:

x=“全局”;//功能范围:(函数(){x、 //年非“全球”var/let/…x;}());//块作用域(不适用于“var”):{x、 //年非“全球”let/const/…x;}

这对于函数和块范围都是正确的1.

两者之间的差异无功功率,无功功率/功能/功能*声明和/常数/声明是初始化.
前者的缩写为未定义或者在作用域顶部创建绑定时使用(生成器)函数。然而,词汇声明的变量仍然存在未初始化。这意味着引用错误尝试访问时抛出异常。只有当/常数/语句进行求值,前面(上面)的所有内容都称为时间死区.

x=y=“全局”;(函数(){x、 //年未定义y、 //年参考错误:未定义yvar x=“本地”;设y=“局部”;}());

请注意让y;语句用初始化变量未定义喜欢设y=未定义;会有。

这个世俗的死区不是语法位置,而是时间在变量(范围)创建和初始化之间。只要不执行代码(例如函数体或简单的死代码),就可以在声明上方的代码中引用变量,如果在初始化之前访问变量,即使访问代码低于声明,它也会抛出异常(例如,在过早调用的提升函数声明中)。

两者之间有什么区别吗常数在这件事上?

不,就起重而言,它们的工作原理是一样的。它们之间唯一的区别是常数ant必须且只能在声明的缩写部分中指定(常数1=1;,两者都是常数一;以及后来的重新分配,如一=2无效)。

1:无功功率,无功功率当然,声明仍然只在函数级起作用

19
  • 1
    啊,这是暗示。吊装总是在一个范围内进行,而块是一切的范围(除了无功功率,无功功率).
    – 贝吉
    评论 2016年1月9日23:02
  • 34
    我发现有点像设foo=()=>bar;设bar=“bar”;foo();说明所有申报单都已悬挂效果甚至更好,因为由于时间死区,效果并不明显。 评论 2016年7月19日13:59
  • 2
    我想问一下如何在let(即闭包)之前声明的函数中引用let定义。我认为这回答了这个问题,这是合法的,但如果在执行let语句之前调用函数,则会出现ref错误,如果以后调用函数,也可以。如果这是真的,也许可以添加到答案中? 评论 2016年10月25日18:07
  • 2
    @MikeLippert是的,没错。在初始化变量之前,不得调用访问该变量的函数。例如,这种情况发生在每个已提升的函数声明中。
    – 贝吉
    评论 2016年10月25日18:23
  • 1
    做出的决定常数喜欢是一个设计缺陷。在一个范围内,常数应在进入时进行吊装和即时初始化。真的,他们应该有一个常数,一个,和另一个关键字,该关键字创建一个类似于“只读”的变量. 评论 2017年3月22日20:27
116

引用ECMAScript 6(ECMAScript2015)规范,常数声明第节,

变量是在实例化其包含的Lexical Environment时创建的,但在计算变量的LexicalBinding之前,不能以任何方式访问.

所以,要回答你的问题,是的,常数提升,但在运行时评估实际声明之前无法访问它们。

1
  • 7
    换句话说,我们可以这样说吗只挂起声明,而不挂起初始化/赋值
    – 查希尔
    评论 2020年10月5日10:11
35

欧洲标准6介绍产生的变量块级范围界定.截止日期欧洲标准5我们没有块级范围,因此在块中声明的变量总是已吊装功能级别范围。

基本上范围指的是变量在程序中的可见位置,它决定了允许您在哪里使用已声明的变量。欧洲标准5我们有全局范围、函数范围和try/catch范围,使用欧洲标准6我们还通过使用Let获得块级范围。

  • 使用定义变量时无功功率,无功功率关键字,它从定义的那一刻起就知道了整个函数。
  • 使用定义变量时语句,它只在它定义的块中已知。

    函数doSomething(arr){//我在这里是已知的,但没有定义//这里不知道j控制台.log(i);控制台日志(j);对于(var i=0;i<arr.length;i++){//我在这里很出名}//我在这里很出名//这里不知道j控制台.log(i);控制台日志(j);for(设j=0;j<arr.length;j++){//这里已知j}//我在这里很出名//这里不知道j控制台.log(i);控制台日志(j);}doSomething([“Thalaivar”,“Vinoth”,“Kabali”,“Dinesh”]);

如果运行代码,您可以看到变量j个只有在而不是之前和之后。然而,我们的变量整个函数从它被定义的那一刻起。

使用let还有另一个很大的优势创建一个新的词汇环境,并绑定新的值,而不是保留旧的引用。

对于(var i=1;i<6;i++){setTimeout(函数(){控制台.log(i);},1000)}for(设i=1;i<6;i++){setTimeout(函数(){控制台.log(i);},1000)}

第一个对于循环始终打印最后的值,具有它创建了一个新的范围并绑定了打印给我们的新值1, 2, 3, 4, 5.

即将到达常数,它的工作原理基本上是,唯一的区别是它们的值不能改变。以常量表示允许变异,但不允许重新分配。

常量foo={};foo.bar=42;console.log(foo.bar)//作品常量名称=[]name.push(“葡萄酒”);console.log(名称)//作品常数=100;年龄=20岁//引发Uncaught TypeError:赋值给常量变量。console.log(年龄);

如果常量是指对象,它将始终引用对象但是对象它本身是可以更改的(如果它是可变的)。如果你想拥有一个不变的对象,您可以使用对象冻结([])

1
  • 如果你没有回答真正的问题变量被提升了,为什么不能访问它们?或者,如果在申报之前无法接触到它们,我们如何证明它们已被吊起。 评论 2021年10月12日19:28
12

根据ECMAScript®2021

出租和建造声明

  • let和const声明定义了作用域为运行执行上下文的LexicalEnvironment的变量。
  • 变量是在实例化其包含的环境记录时创建的,但在计算变量的LexicalBinding之前,不能以任何方式访问这些变量。
  • 当计算LexicalBinding时,由带有Initializer的LexicalBinding定义的变量被赋予其Initializer的AssignmentExpression的值,而不是在创建变量时.
  • 如果在let声明没有Initializer-当评估LexicalBinding时,变量被分配了未定义的值.

块声明实例化

  • 当计算块或案例块时,将创建一个新的声明性环境记录,并在环境记录中实例化块中声明的每个块范围变量、常量、函数或类的绑定。
  • 无论控制如何离开区块,词汇环境总是恢复到以前的状态.

顶级词汇声明名称

在函数或脚本的顶层,函数声明被视为var声明,而不是词汇声明。

结论

  • let和const被提升,但未初始化。

    在变量声明之前引用块中的变量会导致ReferenceError,因为变量处于“暂时死区”从块的开始直到处理声明.

下面的示例清楚地说明了“let”变量在词法范围/嵌套逻辑范围中的行为。

示例1

变量a;控制台.log(a)//未定义控制台.log(b)//未定义变量b;设x;控制台.log(x)//未定义控制台.log(y);//Uncaught ReferenceError:y未定义让y;

变量“y”给出referenceError,这并不意味着它没有被提升。变量是在实例化包含环境时创建的。但它可能不会被访问,因为它处于一个无法访问的“时间死区”。

示例2

让mylet=“我的价值”;(函数(){//让我来;console.log(mylet);//“我的价值”mylet='本地值';})();

示例3

让mylet=“我的价值”;(函数(){让我来;console.log(mylet);//未定义mylet='本地值';})();

在示例3中,函数中新声明的“mylet”变量在log语句之前没有Initializer,因此值为“undefined”。

来源

欧洲货币管理局 中密度神经网络

7

发件人MDN web文档:

在ECMAScript 2015中,常数已提升但未初始化。在变量声明之前引用块中的变量会导致引用错误因为从块的开始到声明被处理,变量处于“暂时死区”。

控制台.log(x);//引用错误设x=3;
2
  • 2
    它是未定义即使在以下情况下无功功率,无功功率。因为,声明是挂起的,而不是初始化。如果你先初始化->访问->声明,如果无功功率,无功功率,它将被提升,以防常数,会有引用错误不会被提升。
    – 查希尔
    评论 2020年10月5日10:17
  • 我认为@Zaheer就在这里;你回答的语言很模糊。你在引用x个在这之前已初始化但是,由于吊装,在声明之前,所以“在变量声明之前引用块中的变量“如果不是不准确的话,在这种情况下也是不准确的。此外,“时间死区”是由于变量尚未已初始化--“已处理”这个词似乎太不精确了。
    – 皱褶
    评论 2022年12月22日21:24
4

在es6中,当我们使用let或const时,必须在使用它们之前声明变量。例如1-

//这会管用的u=10;变量u;//这会产生一个错误k=10;设k;//ReferenceError:初始化之前无法访问“k”。

例如2-

//此代码的工作原理是在使用变量j之前声明它。函数doSmth(){j=9;}设j;doSmth();控制台.log(j);//9
2

我想这可能会解决这个问题

在JavaScript中,用常数被挂起,但它们的行为将其与声明为无功功率,无功功率.

提升意味着在编译阶段将变量声明移到其包含函数或块范围的顶部。然而,两者之间有一个关键区别无功功率,无功功率,、和常数就吊装而言:

  1. 无功功率,无功功率变量被提升并使用值初始化未定义。这意味着您可以引用无功功率,无功功率变量,它将具有值未定义在那一点上。
控制台.log(x);//未定义var x=10;
  1. 常数变量也被挂起,但它们没有初始化。如果在声明之前尝试访问它们,则会得到“ReferenceError”
控制台.log(x);//ReferenceError:未定义x设x=10;

所以,虽然两者都是常数被提升到其块或功能范围的顶部,由于临时死区(TDZ)行为,您无法在其声明之前访问它们。引入此行为是为了捕捉潜在的错误并使代码更具可预测性。

时间死区(TDZ)

暂时死区(TDZ)是JavaScript中的一个概念,与使用常数关键字。这是JavaScript执行上下文中的一个阶段,发生在变量初始化期间,然后为变量赋值。理解TDZ对于避免代码中的意外行为至关重要。

以下是暂时死区的工作原理:

  1. 变量声明:使用声明变量时常数,JavaScript引擎在当前范围内为该变量设置绑定。这意味着引擎知道该变量,但尚未初始化。

  2. 初始化:用声明的变量常数在TDZ中保持未初始化状态,直到使用=操作员。

  3. 初始化前访问:试图访问或引用用声明的变量常数在赋值之前,将导致引用错误这是因为变量存在于TDZ中,不允许在此状态下访问它。

下面是一个示例来说明时间死区:

控制台.log(x);//引发ReferenceError设x=10;

在这个示例中,我们尝试记录x个在声明和初始化之前。这将导致ReferenceError,因为x个控制台.log()声明。

为了避免临时死区,在使用变量之前,最好始终在当前范围的顶部声明变量。例如:

设x;//在范围顶部声明x控制台.log(x);//未定义(无错误)x=10;//初始化x

在这个修改后的代码中,x个在作用域的顶部声明,这意味着当我们记录它时,它仍在TDZ中,但它不会抛出错误,因为我们没有尝试在声明之前访问它的值。

因此,暂时死区是JavaScript中的一种机制,它阻止您访问用声明的变量常数在初始化之前。它有助于捕捉与变量使用相关的潜在错误,并通过在变量声明各自作用域的开头推广变量声明来鼓励更好的编码实践。

0

莱特和康斯特也被吊起。但由于以下原因,如果在初始化变量之前读取了用let或const声明的变量,则会引发异常。

  • 与var不同,它们在吊装时不会使用默认值进行初始化。
  • 在完全初始化之前,无法读取/写入它们。

你的答案

单击“发布您的答案”,表示您同意我们的服务条款并确认您已阅读我们的隐私政策.

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