从一道面试题说起

请说出 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)

let/const 的变量提升与暂时性死区相关推荐

  1. JS---对var,let变量提升和暂时性死区(TDZ)的理解

    一.var和let声明的变量所属作用域不同 var声明的变量属于函数作用域,而let声明的变量属于块级作用域 二.var和let声明的变量所属作用域不同,相应的变量提升也不同 var声明的变量 在函数 ...

  2. Javascript变量提升与暂时性死区

    暂时性死区:在ES6之前,使用 typeof 运算符操作一个未声明的变量时,不会报错,该变量的值以 undefined 作处理.而在ES6之后,使用ES6的变量声明方法(let, const, cla ...

  3. JS:变量提升与临时性死区TDZ

    一.解析过程 js运行前会有编译解析过程,有些错误会在编译过程中被发现. <body><script>var web = 'yooo';console.log(web);var ...

  4. JS-变量提升与暂时性死区概念

    变量提升(Hoisting) 变量提升(Hoisting)被认为是, Javascript中执行上下文 (特别是创建和执行阶段)工作方式的一种认识.在 ECMAScript® 2015 Languag ...

  5. 【ES6】let、const变量提升的验证,以及TDZ死区的理解

    前段时间在网上自学ES6,了解到ES6新增的let和const是存在变量提升的,但是由于TDZ(暂时性死区)的存在,在定义前访问会出现错误. 今天在腾讯课堂听老师讲ES6,多次强调let和const没 ...

  6. JS中关于let(const)暂时性死区的分析笔记

    let的暂时性死区 const与let在这个地方原理上差不多,故以let为例进行分析 1.什么是暂时性死区 首先来看一段代码 console.log(a); // undefined var a = ...

  7. var let const声明变量的区别

    在js中定义变量的方式有三种,其中let和const关键字是来自ES6中的,下面将逐一介绍各个关键字声明变量的特点. var声明变量 var 是一个 JS关键字,用来声明变量( variable 变量 ...

  8. js 详解es6 let TDZ(暂时性死区)

    暂时性死区产生的原因: ES6 明确规定,如果区块中存在 let 和 const 语句(注意:let.const语句不存在变量提升),这个区块对这些命令声明的变量,从一开始就形成了封闭作用域.凡是在声 ...

  9. Js什么是暂时性死区

    在Js中什么是暂时性呢? 暂时性死区是针对'const','let'这两个关键字而产生的概念.首先变量提升这个js的基本概念无法撼动,'const'和'let'作为块级作用域也不能避免.和'var'不 ...

最新文章

  1. 办公word,ppt,excel问题
  2. 015_CSS伪元素选择器
  3. spo2数据集_Arduino 血氧心率模块传感器数据采集
  4. maya python 弹出窗口_maya python打印状态复选框?
  5. 1.使用C++封装一个链表类LinkList
  6. ubuntu12.04配置双显示器
  7. 百万数据报表:分析以及解决办法
  8. 去除Activity的标题栏以及全屏显示
  9. TypeScript里的类型为any和泛型的区别
  10. 热榜!基于jsp+mysql的JSP在线水果销售商城系统设计实现【建议收藏】
  11. Asp.net中关于上传文件的各项基本操作
  12. Android TextView setGravity不起作用
  13. 【戴嘉乐】(进阶)基于IPFS和Ngrok构建自维护资源网关
  14. SAP中计划日历计算规则的理解
  15. adb之am、pm命令
  16. 微信小程序 使用canvas绘图
  17. 串口DCB定义,配置例程
  18. 活动预告丨易盾CTO朱浩齐将出席2018 AIIA大会,分享《人工智能在内容安全的应用实践》...
  19. Bribe the Prisoners——GCJ 2009 Round1C C(区间dp)
  20. Python 替换文件中内容

热门文章

  1. 写篇文章告诫自己2021继续前进
  2. ADB 调试手机的三种方式(USB、WLAN、WIFI)
  3. 最新消息!苹果手机也支持微信多开
  4. Miscellaneous | 转换字符串到整数
  5. 标准linux休眠和唤醒机制分析
  6. windows 用户变量和系统变量的区别
  7. 远程桌面无法复制粘贴的解决方法
  8. android 图片文字布局,Android代码实现图片和文字上下布局
  9. DQL查询数据(最重要)
  10. 【STK】手把手教你利用STK进行弹道导弹和运载火箭发射仿真的相关方法全过程解析