不要在控制台上使用 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 ...
随机推荐
- Apache配置
redhat6.4企业版用的centos的yum源. 下面进行apache的安装与配置. 1.yum在线安装Apache 一般不采用yum在线安装因为如果apache坏掉了,yum安装过程中会有依赖的 ...
- WIN7下django1.8下载安装
前言:公司电脑上django是在pycharm上下载自动安装的,家里电脑没安pycharm,所以自己手动安装. django下载地址:https://www.djangoproject.com/dow ...
- hdu5317 RGCDQ (质因子种数+预处理)
RGCDQ 题意:F(x)表示x的质因子的种数.给区间[L,R],求max(GCD(F(i),F(j)) (L≤i<j≤R).(2<=L < R<=1000000) 题解:可以 ...
- python3 下的文件输入输出特性以及如何覆盖文件内容和接下去输入
今天碰到了一个非常有意思的python特性.本来我是想打开一个文件,在文件的末尾接下去输入一些内容的,代码如下: f = open('test.txt', 'r+') f.write(content) ...
- 抓鼠标的猫(Win32实现,Codeblocks+GCC编译)
程序效果: 猫的眼睛一直跟着鼠标移动: 鼠标经过猫的右脚附近时,猫会抓住鼠标.(未使用Hook) 代码: //main.cpp 1 #include <windows.h> #includ ...
- AngularJS之开发组件的一些思路
欢迎大家指导与讨论 : ) 一 .前言 由于笔者水平有限,在这里只是分享自己开发组件的一些思路~ 摘要:无UI组件类.有UI组件类.有UI组件类型2.欢迎拍砖吐槽 O(∩_∩)O 二 .无UI组件类 ...
- Python-18-Django 基础篇
1. Web 框架介绍 具体介绍Django之前,必须先介绍WEB框架等概念. Web框架:通俗地讲,就是别人已经设定好的一个web网站模板,你学习它的规则,然后「填空」或「修改」成你自己需要的样子. ...
- 几种display:table-cell的应用
一.display:table-cell属性简述 display:table- cell属性指让标签元素以表格单元格的形式呈现,类似于td标签.目前IE8+以及其他现代浏览器都是支持此属性的,但是IE ...
- 取文件MD5 WINAPI
#include <windows.h> #include <wincrypt.h> #include <stdio.h> BOOL GetFileHash(LPC ...
- Ubuntu 下配置apache和APR
软件环境:ubuntu14.04 虚拟机Vmware 软件:http://httpd.apache.org/ httpd-2.2.29.tar.gz 不需要单独下载APR. 1.解压apach ...