考虑下面的这三句代码和对应的报错信息:

假设写这个代码的人一开始不知道 ES6 里新增的构造函数不能省略 new,于是第一行写错了。然后第二行尝试重新声明一次,结果又报错说重复声明了。那干脆不声明,直接赋值总行吧,结果又报错说 map 未定义。

这三个报错直接对应规范里的下面三条规则(并附通俗解释):

23.1.1.1 Map()

1. If NewTarget is undefined, throw a TypeError exception.

解释:Map() 不带 new 不能被调用。

15.1.11 GlobalDeclarationInstantiation()

5.b. If envRec.HasLexicalDeclaration(name) is true, throw a SyntaxError exception.

解释:map 已经被声明过,不能重复声明。

8.1.1.1.5 SetMutableBinding()

4. If the binding for N in envRec has not yet been initialized, throw a ReferenceError exception.

解释:map 处于已经声明但未初始化的状态,这种状态不能通过 = 赋值。

第二个报错的疑问点:第一次声明等号右侧报错了,map 怎么会声明成功呢?

一段代码在被真正的执行前,会有个专门用来声明变量的过程,俗语常把这个过程称为预解析/预处理。无论是用 var 还是用 let/const 声明的变量,都是在这个过程里被提前声明好的,俗语常把这种表现称为 hoisting。只是 var 和 let/const 有个区别,var 变量被声明的同时,就会被初始化成 undefined,而后两者不会。

第三个报错的疑问点:没有初始化我用 = 给它初始化还不行吗?还有为什么报错是 “map 未定义”?

首先说 “map 未定义”是 V8 的报错信息不友好,正确的报错信息应该是 “map 未初始化”。

规范规定一个已经声明但未初始化的变量不能被赋值,甚至不能被引用,代码示例里第三句即便只写一个 map 也会报一样的错。规范里用来声明 var/let 变量的内部方法是 CreateMutableBinding(),初始化变量用 InitializeBinding(),为变量赋值用 SetMutableBinding(),引用一个变量用 GetBindingValue()。在执行完 CreateMutableBinding() 后没有执行 InitializeBinding() 就执行 SetMutableBinding() 或者 GetBindingValue() 是会报错的,这种表现有个专门的术语(非规范术语)叫 TDZ(Temporal Dead Zone),通俗点说就是一个变量在声明后且初始化前是完完全全不能被使用的。

因为 var 变量的声明和初始化(成 undefined )都是在“预处理”过程中同时进行的,所以永远不会触发 TDZ 错误。let 的话,声明和初始化是分开的,只有真正执行到 let 语句的时候,才会被初始化。如果只声明不赋值,比如 let foo,foo 会被初始化成 undefined,如果有赋值的话,只有等号右侧的表达式求值成功(不报错),才会初始化成功。一旦错过了初始化的机会,后面再没有弥补的机会。这是因为赋值运算符 = 只会执行 SetMutableBinding(),并不会执行 InitializeBinding(),所以例子中的 map 变量被永远困在了 TDZ 里。

其实我举的这个例子已经在 FirefoxChromeNode 的 bug 平台上都被反应过了。Firefox 的 JS 引擎为了消除这种奇怪的表现,专门针对 shell 环境(包括 Firefox 中的控制台)做了特殊处理,当 let/const 语句等号右侧的表达式求值发生错误后,引擎会把它初始化成 undefined:

如果是 js shell 的话,还能看到一段解释信息,表明这样做其实是违反规范的:

读到现在,有同学就问了:“就因为这个就不让在控制台里用 let/const?我以后记得加 new 不就得了”。等号右边的表达式报错其实有很多种情况,比如某个属性意外成了 undefined,比如右侧的函数调用本身报错了,都有可能,出错其实挺常见的。

而且除了这种因报错导致你不得不重新声明一次的情况,还有一些情况是你主动想重复声明的。比如我们经常在控制台里写代码都是想最终产出一段代码的,但你写的时候是一句是一句写的,写一句回车执行,没问题的话,按下上箭头,然后按 shift+enter,换行后写第二句,可能最终完成需要十来句。如果其中某一句用到了 let/const,第二次执行的时候就会报错,然后你只能刷新页面了。

不用 let/const 那用啥呢?用 var 或者直接用赋值语句都可以,依情况而定。而且本文的观点并不是绝对的,很多情况下是可以用 let/const 的,比如你的声明语句是写在一个函数里的,比如你从别的地方复制了一个脚本(抢月饼?),只需要在控制台粘贴执行一次,不用修改,这些情况用什么都可以。

不要在控制台上使用 let/const的更多相关文章

  1. Unity 实现Log实时输出到屏幕或控制台上<一>

    本文章由cartzhang编写,转载请注明出处. 所有权利保留. 文章链接:http://blog.csdn.net/cartzhang/article/details/49818953 作者:car ...

  2. .NET Core下的日志(3):如何将日志消息输出到控制台上

    当我们利用LoggerFactory创建一个Logger对象并利用它来实现日志记录,这个过程会产生一个日志消息,日志消息的流向取决于注册到LoggerFactory之上的LoggerProvider. ...

  3. eclipse:eclipse for java EE环境下如何配置tomcat服务器,并让tomcat服务器显示在控制台上,将Web应用部署到tomcat中

    eclipse环境下如何配置tomcat 打开Eclipse,单击"Window"菜单,选择下方的"Preferences".  单击"Server& ...

  4. c/c++ 拷贝控制 右值与const引用

    拷贝控制 右值与const引用 背景:当一个函数的返回值是自定义类型时,调用侧用什么类型接收?? 1,如果自定义类型的拷贝构造函数的参数用const修饰了:可以用下面的方式接收. Test t2 = ...

  5. Ubuntu上运行Blender,在控制台上查看运行结果

    1.首先在控制台打开Blender. 具体操作:找到Blender的安装路径,我的是:/home/lcx/下载/blender-2.78c-linux-glibc219-x86_64 $cd /hom ...

  6. Java:IO流的综合用法(从键盘录入数据并打印在控制台上)

    import java.io.*; public class IOTestDouble { public static void main(String[] args)throws Exception ...

  7. java 在控制台上输入密码时,密码不显示在控制台上

    用下面的方法可以实现在控制台上输入密码时,密码不显示在控制台上:Console cons=System.console(); System.out.print(" 密码:"); c ...

  8. 大数据调错系列之hadoop在开发工具控制台上打印不出日志的解决方法

    (1)在windows环境上配置HADOOP_HOME环境变量 (2)在eclipse上运行程序 (3)注意:如果eclipse打印不出日志,在控制台上只显示 1.log4j:WARN No appe ...

  9. 黑马基础阶段测试题:通过字符输入流读取info.txt中的所有内容,每次读取一行,将每一行的第一个文字截取出来并打印在控制台上。

    package com.swift; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File ...

随机推荐

  1. spf13-vim安装与使用

    一.简介 spf13-vim是Vim插件与配置的一个发行版本,包含了一整套精心挑选的Vim插件,采用Vundle进行插件管理,并且可以通过下列文件进行个性化配置 ~/.vimrc.local #个性化 ...

  2. 断言(Assert)与异常(Exception)

    断言是被用来检查非法情况而不是错误情况,即在该程序正常工作时绝不应该发生的非法情况,用来帮助开发人员对问题的快速定位.异常处理用于对程序发生异常情况的处理,增强程序的健壮性.容错性,减少程序使用中对用 ...

  3. netty3升netty4一失眼成千古恨

    老项目是netty3的,本来想直接改到netty5,但是netty5居然是只支持jdk1.7,很奇怪jdk1.6和jdk1.8都不行..为了兼容jdk1.6加上netty4本来和netty5就差别不大 ...

  4. Netruon 理解(11):使用 NAT 将 Linux network namespace 连接外网

    学习 Neutron 系列文章: (1)Neutron 所实现的虚拟化网络 (2)Neutron OpenvSwitch + VLAN 虚拟网络 (3)Neutron OpenvSwitch + GR ...

  5. winform/窗体鼠标事件编程中的几个问题

    1.进行.net窗体的开发,经常用到鼠标事件,如MouseDown/MouseUp/MouseMove/MouseClick等.可是有时候给控件添加鼠标事件,就是不响应,怎么办呢! 答案:1.控件是否 ...

  6. (转)java字节流和字符流的区别

    转载: http://www.cnblogs.com/dolphin0520/category/361055.html 字节流与和字符流的使用非常相似,两者除了操作代码上的不同之外,是否还有其他的不同 ...

  7. Python版本2.7切3.5和3.5切2.7

    在Ubuntu上是自带Python2.7和3.5的 当你在终端输入Python的时候是显示Python2.7的 叫大家你怎么切换到Python3.5版本 1,查看是否存在python3.5终端输入 c ...

  8. Apache、nginx配置的网站127.0.0.1可以正常访问,内外网的ip地址无法访问,谁的锅?

    最近做开发,发现一个比较尴尬的问题.因为我是一个web开发者,经常要用到Apache或者nginx等服务器软件,经过我测试发现,只要我打开了adsafe,我便不能通过ip地址访问我本地的网站了,比如我 ...

  9. HDU 1729 Stone Game【SG函数】

    以下转载至:长春理工大学赵小舟博弈论ppt 题目大意: 1.有n个盒子,每个盒子都有它的容量s 2.在游戏开始时,每个盒子里都有一些石子 3.双方轮流进行游戏,向一个盒子投入n个石子,其中n不能大于当 ...

  10. Windows 10 装机回忆录

    Frank.Han 标记: Windows,快捷键,安装 2015年10月我便更新了Win10系统,一直用着很顺手,比起Win8.x,他更像Win7的嫡系版本. 屏蔽掉系统自带的平板服务(小娜.地理位 ...