不要在控制台上使用 let/const
考虑下面的这三句代码和对应的报错信息:

假设写这个代码的人一开始不知道 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 里。
其实我举的这个例子已经在 Firefox、Chrome、Node 的 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的更多相关文章
- Unity 实现Log实时输出到屏幕或控制台上<一>
本文章由cartzhang编写,转载请注明出处. 所有权利保留. 文章链接:http://blog.csdn.net/cartzhang/article/details/49818953 作者:car ...
- .NET Core下的日志(3):如何将日志消息输出到控制台上
当我们利用LoggerFactory创建一个Logger对象并利用它来实现日志记录,这个过程会产生一个日志消息,日志消息的流向取决于注册到LoggerFactory之上的LoggerProvider. ...
- eclipse:eclipse for java EE环境下如何配置tomcat服务器,并让tomcat服务器显示在控制台上,将Web应用部署到tomcat中
eclipse环境下如何配置tomcat 打开Eclipse,单击"Window"菜单,选择下方的"Preferences". 单击"Server& ...
- c/c++ 拷贝控制 右值与const引用
拷贝控制 右值与const引用 背景:当一个函数的返回值是自定义类型时,调用侧用什么类型接收?? 1,如果自定义类型的拷贝构造函数的参数用const修饰了:可以用下面的方式接收. Test t2 = ...
- Ubuntu上运行Blender,在控制台上查看运行结果
1.首先在控制台打开Blender. 具体操作:找到Blender的安装路径,我的是:/home/lcx/下载/blender-2.78c-linux-glibc219-x86_64 $cd /hom ...
- Java:IO流的综合用法(从键盘录入数据并打印在控制台上)
import java.io.*; public class IOTestDouble { public static void main(String[] args)throws Exception ...
- java 在控制台上输入密码时,密码不显示在控制台上
用下面的方法可以实现在控制台上输入密码时,密码不显示在控制台上:Console cons=System.console(); System.out.print(" 密码:"); c ...
- 大数据调错系列之hadoop在开发工具控制台上打印不出日志的解决方法
(1)在windows环境上配置HADOOP_HOME环境变量 (2)在eclipse上运行程序 (3)注意:如果eclipse打印不出日志,在控制台上只显示 1.log4j:WARN No appe ...
- 黑马基础阶段测试题:通过字符输入流读取info.txt中的所有内容,每次读取一行,将每一行的第一个文字截取出来并打印在控制台上。
package com.swift; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File ...
随机推荐
- PKCS#1规范阅读笔记1--------基本概念
规范中有很多数学相关的推演和计算,并不打算在这里介绍,主要介绍一下相关的计算流程及最终的签名结果. 算法可以分为:对称算法和非对称算法两大类.对称算法加密和解密都用的是同一个密钥:而非对称算法却是有一 ...
- JS入门学习,写一个简单的图片库
<!-- 新手刚开始学JS,每天坚持写点东西 坚持下去,希望能有所进步 . 加油~~ --> <!DOCTYPE html> ...
- Struts2 使用jQuery实现Ajax
在jQuery中将Ajax相关的操作进行封装,使用时只需在合适的地方调用Ajax相关的方法即可,相比而言,使用jQuery实现Ajax更加简洁,方便 1.$.Ajax()可以通过发送Http请求加载远 ...
- PHP unset销毁变量并释放内存
PHP的unset()函数用来清除.销毁变量,不用的变量,我们可以用unset()将它销毁.但是某些时候,用unset()却无法达到销毁变量占用的内存!我们先看一个例子: <?php $s=st ...
- 通过trie树单词自动补全(二)
经常使用iciba进行单词查询, 关于他的搜索建议是通过单词前缀做的索引, 所以自己想动手实现下, 当然如果借助mysql的话,一条sql语句就能实现, 网上查询了下trie正适合做这个,所以通过C语 ...
- TestFlight
链接: 如何使用TestFlight进行Beta测试 [转]TestFlight APP测试(IOS如何让上架前给其他人测试) 转]TestFlight APP测试(IOS如何让上架前给其他人测试) ...
- ajax实现下拉菜单无刷新加载更多
$(function() { var page = 1; var discount = $('#discount'); var innerHeight = window.innerHeight; va ...
- C#.NET 大型企业信息化系统集成快速开发平台 4.2 版本 - 防止暴力破解密码、提高大型信息系统安全
几十万人使用的系统.覆盖全国.每天营业额上好几个亿的.若信息安全方面太薄弱了.那将会是致命的打击.甚至威胁到企业的正常运转.从国家层面到企业级别大家都在重视信息的安全.可控. 运行速度慢一点点可以忍受 ...
- 【ASP.NET实战教程】ASP.NET实战教程大集合,各种项目实战集合
[ASP.NET实战教程]ASP.NET实战教程大集合,各种项目实战集合,希望大家可以好好学习教程中,有的比较老了,但是一直很经典!!!!论坛中很多小伙伴说.net没有实战教程学习,所以小编连夜搜集整 ...
- javascript正则表达式:匹配所有html标签
此正则表达式收集于网络,但那个网站连复制一下都需要注册会员,鄙视之,所以不提他了.而且,原作者也不可能是那个网站. html标签有<h1></h1>这种成对的,也有<br ...