不要在控制台上使用 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 ...
随机推荐
- openstack api快速入门
原文:http://my.oschina.net/guol/blog/105430 openstack官方有提供api供开发者使用,可以使用api做一些外围的小工具,用来简化对openstack的管理 ...
- Walle - 瓦力上线部署系统搭建攻略
背景知识:Walle - 瓦力是一个支持svn.git.多用户.多项目.多环境同时部署的上线部署系统,http://www.oschina.net/news/68610/walle-0-9-2 实验系 ...
- 最小生成树 kruskal算法 codevs 1638 修复公路
1638 修复公路 时间限制: 1 s 空间限制: 256000 KB 题目等级 : 钻石 Diamond 题解 题目描述 Description A地区在地震过后,连接所有村庄的公 ...
- Serial Port Programming on Linux(转载)
This is a tutorial on how to program the Serial Ports on your Linux box.Serial Ports are nice little ...
- Maven配置详见
CSDN 2016博客之星评选结果公布 [系列直播]零基础学习微信小程序! "我的2016"主题征文活动 博客的神秘功能 maven 配置详解 标签: mave ...
- 文件夹右键添加“DOS”命令
导入注册表 Windows Registry Editor Version 5.00 [HKEY_CLASSES_ROOT\Folder\shell\RunCMD] @="进入命令行&quo ...
- 原生JS实现MVVM模式
欢迎大家指导与讨论 : ) 前言 关于MVVM的原理大家可以参考这篇文章.<[翻译]Object.observe()带来的数据绑定变革 >http://www.tuicool.com/ar ...
- TinyFrame系列:基于EFCodeFirst,IOC,AOP的轻型框架
TinyFrame开篇:基于CodeFirst的ORM TinyFrame续篇:整合Spring IOC实现依赖注入 TinyFrame再续篇:整合Spring AOP实现日志拦截 TinyFrame ...
- ORACLE实验一-三
警告: 创建的触发器带有编译错误. SQL> show error;TRIGGER TRG_SCORE_AFT_ROW 出现错误: LINE/COL ERROR-------- -------- ...
- AppBox升级进行时 - Entity Framework的增删改查
AppBox 是基于 FineUI 的通用权限管理框架,包括用户管理.职称管理.部门管理.角色管理.角色权限管理等模块. Entity Framework新增数据 以新增用户为例,作为对比,先来看下使 ...