let/const 的变量提升与暂时性死区

let/const 的变量提升与暂时性死区在面试或一些文章中提到var和let/const区别时,总说var有变量提升,let/const不存在变量提升,这种说法是错误的.var和let/const都有变量提升,但是let/const暂时性死区的存在要求调用该类变量前必须先经过显式赋值

大家好,又见面了,我是你们的朋友全栈君。

从一道面试题说起

请说出 let,const,var 的区别

大部分的回答是这样的,甚至很多博客中的答案也是这样的:

  1. let/const 提供了块级作用域
  2. let 不能重复定义
  3. var 有变量提升,let / const 没有变量提升

前两条没什么问题,第三条中 var 有变量提升 也是对的,而 let / const 没有变量提升 确是错误的,且有很大的迷惑性。本文就从这一点谈起:

let / const 存在变量提升(hoist)以及它们的暂时性死区(TDZ)

let/const 没有变量提升的错觉

console.log(aVar); // undefined
console.log(aLet); // causes ReferenceError: aLet is not defined
var aVar = 1;
let aLet = 2;

var 与 let 的对比式运行结果,强烈的衬托出 let 似乎没有变量提升,否则为什么会有 aLet is not defined 报错

let/const 变量提升的反例

let x = 'outer value';
(function() { 
   
  console.log(x);
  let x = 'inner value';
}());

为什么说上述例子可以证明 let 会发生变量提升呢

我们先把这个例子修改一下,注释掉 let x = ‘inner value’
在这里插入图片描述
运行程序,会打印出 outer value. 这是因为函数的作用域链. 在闭包内没有找到 x 的定义,沿着函数作用域链寻到外层关于 x 的定义。

接下来我们去掉注释,运行程序在这里插入图片描述
报错,结合上述的例子,可以得出两个结论:

  1. 在闭包内,报错显示:在初始化前不允许读取x (注意报错不是 x not defined)
  2. 在闭包外,并未沿着函数作用域链找到外层 x 的定义.

可见,由于 let x = ‘inner value’ 在闭包作用域内的变量提升,阻断了函数作用域链的向上延伸。但在闭包内部,尽管 x 发生了变量提升,但是在初始化赋值前(before initialization)不允许读取。

这就引出了本文要谈的下一个概念: 暂时性死区 (TDZ)

暂时性死区

Temporal Dead Zone (TDZ) 翻译为中文即为 暂时性死区

先看一段 MDN 上关于暂时性死区的定义

let bindings are created at the top of the (block) scope containing the declaration, commonly referred to as “hoisting”. Unlike variables declared with var, which will start with the value undefined, let variables are not initialized until their definition is evaluated. Accessing the variable before the initialization results in a ReferenceError. The variable is in a “temporal dead zone” from the start of the block until the initialization is processed.

文档第一句话就明确指出 let 存在变量提升,但是与 var 不同的是,var 的变量提升的同时会默认赋值为 undefined. 而 let 仅仅发生了提升而已,并不会赋任何值给变量,在显式赋值之前,任何对变量的读写都会导致 ReferenceError 的报错。从代码块(block)起始到变量求值(包括赋值)以前的这块区域,称为该变量的暂时性死区

图示如下:在这里插入图片描述

暂时性死区深入探讨

到目前为止,我们对暂时性死区的理解已经够用了,下面是从 ECMA262 let/const 标准 层面来再稍微深入的探讨一下这个问题。

先看一下文档中关于 let 和 const 的一段描述:
在这里插入图片描述
Lexical Environment:词法环境
Lexical Binding: 词法绑定

试着把文档中的关键句翻译成人话:

The variables are created when their containing Lexical Environment is instantiated…

当程序控制流程运行到特定作用域(scope ≈ Lexical Environment) 时:即模块,函数,或块级作用域。在该作用域中代码真正执行之前,该作用域中定义的 let 和 const 变量会首先被创建出来。正是所谓的变量提升!

…but may not be accessed in any way until the variable’s LexicalBinding is evaluated…

这其实揭示了暂时性死区的原理: 在 let/const 变量被赋值(LexicalBinding)以前是不可以读写的。

If a LexicalBinding in a let declaration does not have an Initializer the variable is assigned the value undefined when the LexicalBinding is evaluated.

如果 let/const 变量未被显式赋值(Initializer),默认值即为 undefined
在这里插入图片描述

更深入的例子

变量自我赋值

运行失败: 在 let foo = (foo + 55) 所在的 block 作用域中,表达式右侧 foo 的值尚在 TDZ,即没有显式赋值。
在这里插入图片描述

ES6 函数默认参数的 TDZ

运行失败: 函数的参数列表可以看作一个 scope,且参数是从左向右解析的。当 ‘a’ 在赋值时试图获取 ‘b’ 的值,而此时 ‘b’ 出于 TDZ 状态,因此程序报错。
在这里插入图片描述
运行成功: ‘b’ 在赋值时需要获取 ‘a’ 的值,在此之前 ‘a’ 已经被显式赋值为1,不存在 TDZ 的问题。
在这里插入图片描述

总结

var, let/const 都有变量提升,let/const 存在暂存性死区 (TDZ)

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/149664.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • ROC曲线及AUC值[通俗易懂]

    ROC曲线及AUC值[通俗易懂]ROC曲线参考文献:【ROC曲线与AUC值】1.介绍及引入ROC的全名叫做ReceiverOperatingCharacteristic,其主要分析工具是一个画在二维平面上的曲线——ROCcurve。平面的横坐标是falsepositiverate(FPR),即假阳性率(1−Sp1-Sp1−Sp);纵坐标是truepositiverate(TPR),即真阳性率(SnSnSn)。对某个分类器而言,我们可以根据其在测试样本上的表现得到一个TPR和FPR点对。这样,此分类器就可以映射成

    2022年5月16日
    60
  • Python fill_python mean

    Python fill_python mean而df.fillna(0)用0填充所有NA/NaN值,是否有一个函数将所有非NA/NaN值替换为另一个值,例如1?如果我的DataFrame中的值是可变长度列表,那么:>df.replace()要求列表长度相同>布尔索引,如df[len(df)>0]=1抛出ValueError:无法插入True,已经存在>pandas.get_dummies()抛出Ty…

    2022年8月12日
    9
  • PLD,CPLD,FPGA区别[通俗易懂]

    PLD,CPLD,FPGA区别[通俗易懂]入门以后可以学习Xilinx的ISE,Altera的QuartusII学习CPLD初学者,建议选用LATTICE,这家公司在此方面有优势主流还是Altera和Xilinx,毕竟是最大的两家PLD公司(Cyclone   Spartan) PLD,CPLD,FPGA有何不同?不同厂家的叫法不尽相同,  PLD(ProgrammableLogicDevice)是可编程逻辑器件的总称

    2022年5月4日
    95
  • Spug – 轻量级自动化运维平台

    Spug – 轻量级自动化运维平台Spug-轻量级自动化运维平台对于中小型企业而言,进行主机和应用的管理是比较麻烦的,应用部署往往需要直接连接服务器,再进行手动的环境配置、代码拉取、应用构建和部署发布等工作,容易出错,且耗时费力。一个好的自动化运维平台,往往能大大节省人力物力,提高开发部署效率。Spug,正是一个面向中小型企业设计的轻量级自动化运维平台。Spug自动化运维平台简介Spug,是openspug在Github上开源的自动化运维平台,项目位于https://github.com/openspug/spug,

    2022年5月17日
    108
  • matlab分段函数怎么画图_关于MATLAB中分段函数的画法[通俗易懂]

    matlab分段函数怎么画图_关于MATLAB中分段函数的画法[通俗易懂]关于MATLAB中分段函数的画法最近拿到一题关于MATLAB的分段函数画法的题目,我在网上找了挺久,但没发现很多有用的资料.所以感觉很棘手.但是问题还是要解决,所以我就自己整理了些东西,不怕大家见笑.我把这些分段函数分为两类:一.对于y=f(x)这个模型来讲,一类是关于其中一个段是y为常量的一个模型,举例说明.例1.y={0,(x<0);1,(x>=0)};在x>-10&…

    2022年5月31日
    132
  • 基数排序(LSD+MSD)详解

    基数排序(LSD+MSD)详解一.计数排序二.基数排序

    2022年6月9日
    54

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号