不要在控制台上使用 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 ...
随机推荐
- Java 容器(list, set, map)
java容器类库的简化图: (虚线框表示接口, 实线框表示普通的类, 空心箭头表示特定的类实现了接口, 实心箭头表示某个类可以生成箭头所指的类对象) 继承Collection的主要有Set 和 Lis ...
- 报表开发工具Finereport移动端app js接口列表【全】
应用报表工具Finereport的开发人员会发现其移动端app 同样也推出了很多js接口,那这些接口到底有多少,其移动端又有哪些地方支持调用js,这些接口具体又该如何调用呢.根据我平时的开发经验,给大 ...
- Oracle约束(Constraint)详解
概述 约束是数据库用来确保数据满足业务规则的手段,不过在真正的企业开发中,除了主键约束这类具有强需求的约束,像外键约束,检查约束更多时候仅仅出现在数据库设计阶段,真实环境却很少应用,更多是放到程序逻辑 ...
- 常用算法——排序(三)
希尔排序法 希尔排序又称为缩小增量排序,也属于插入排序类的算法,是对直接插入排序的一种改进. 基本思想就是:将需要排序的序列划分为若干个较小的序列,对这些序列进行直接插入排序,通过这样的操作可使用需要 ...
- Hibernate中的一对一关联
Hibernate提供了两种一对一映射关联关系的方式: 1)按照外键映射 2)按照主键映射 下面以员工账号表和员工档案表(员工账号和档案表之间是一对一的关系)为例,介绍这两种映射关系,并使用这两种 映 ...
- Java并发编程:Lock
原文出处: 海子 在上一篇文章中我们讲到了如何使用关键字synchronized来实现同步访问.本文我们继续来探讨这个问题,从Java 5之后,在java.util.concurrent.locks包 ...
- WPF中监视DependencyProperty的变化
WPF中监视DependencyProperty的变化 周银辉 尽管一个类会提供很多事件,但有时候还是显得不够,比如说前两天我就以为WPF的ListBox控件会有ItemsSourceChange ...
- C++与C# UDP通信实例(同一台PC)
对于同一个PC机而言,服务器端和客户端在一个PC机上面,端口必须要不一样,不然会冲突. 你总不能自己又当爹又当妈吧. 所以在进行程序设计的时候,需要考虑这一点: 在此接口设计中,C++当作UDP的服务 ...
- 配置Windows下的PHP开发环境
一.配置 Apache 开发环境: 二.配置 PHP 开发环境 配置 Apache 开发环境 0. 下载 Apache.由于官方只提供了源码包,我们要么自己编译要么使用别人提供的已经编译好的二进制包. ...
- APIPA(Automatic Private IP Addressing,自动专用IP寻址)
APIPA APIPA(Automatic Private IP Addressing,自动专用IP寻址),是一个DHCP故障转移机制.当DHCP服务器出故障时, APIPA在169.254.0.1到 ...