不为人知的JavaScript自动分号插入机制( ASI )
楔子
之前一直写C,写了一段时间JavaScript之后一直很很好奇一个东西。在C和Java等语言里面,大括号的使用一般都是类似这样的
int main(args[])
{
return 0;
}
- 1
- 2
- 3
- 4
而到JavaScript里面则是这样写
function main(args){
alert("hello");
return 0;
}
- 1
- 2
- 3
- 4
起始的大括号不独占一行了,觉得很疑惑,查了一些资料才知道,这是和JavaScript一个自动修复机制有关系,它总是希望通过自动插入分号来修正有缺损的程序,虽然我不知道这有什么用。
自动插入分号机制
在《JavaScript语言精粹》这本书里,这个机制被划入到了JavaScript的毒瘤里面,与之并列的前面的全局变量。
有些时候,不合时宜地插入分号,会在例如return语句里面导致严重的后果。
如果一个return语句要正确返回一个值,这个值的表达式的开始部分必须和return位于同一行。
我们来看下面这个例子
return
{
status:true;
}
- 1
- 2
- 3
- 4
看起来这是要返回一个包含对象,但是万恶的自动插入分号处理后,返回值变成了undefined
,而且不会报任何的错误和警告。
如果我们把大括号这样处理的话就能避免这个问题
return{
status:true;
};
- 1
- 2
- 3
自动插入分号的详细规则
在es5标准中定义了自动分号插入规则,包括以下三个基本规则加上两个前置条件。
前置条件
1.如果插入分号后解析结果是空语句,那么不会自动插入分号。
例子:
if(i>j)
else k=l
- 1
- 2
这种情况下,if后面else前面是被解析为空语句,所以不加分号
2.如果插入分号后,它会成为for
语句头部的两个分号之一,那么也不会插入分号
例如:
for(a;b
)
- 1
- 2
这种情况下,虽然分行了,但是不会被插入分号。
基本规则
从左向右解析程序的时候,当遇到一个不符合任何语法产生式的token
也就是违规标记的时候,那么只要满足下列条件之一,就会在哪个标记之前自动插入一个分号
1、前一个标记和这个违规标记之前至少存在一个行终止符
2、违规的标记是 }
举个栗子
{1
2}3
{1
;2;}3
- 1
- 2
- 3
- 4
在第一行和第二行的1、2不符合任何产生式,且它们之间有一个行终止符,所以会在数字2之前加分号,在第二行2后面也需要加一个分号,因为后面的违规标记是一个}
左到右解析程序,tokens 输入流已经结束,当解析器无法将输入 token 流解析成单个完整 ECMAScript 程序 ,那么就在输入流的结束位置自动插入分号。
对于受限产生式,也就是下面的5个,我们把产生式 后面的 token 叫做受限 token,如果在 token 和 受限 token 间存在了至少一个行终止符,那么会在受限 token 前自动加上 token。
受限的产生式只限如下5个:
后缀表达式、continue语句、break语句、return语句、throw语句
如何预防这个毒瘤
1、后缀运算符 ++
或 --
和它的操作数应该出现在同一行。
2、return
或throw
语句的表达式开始位置应该和 return
或 throw
token 同一行。
3、break
或 continue
语句的标示符应该和 break
或continue
token 同一行。
最重要的还是多加分号
来自leviscar的小贴士
为啥只执行函数前面要加分号?
例如我之前看到的zepto.js的源码开头
;(function(undefined) {
if (String.prototype.trim === undefined) // fix for iOS 3.2
String.prototype.trim = function() {
return this.replace(/^\s+|\s+$/g, '')
}
- 1
- 2
- 3
- 4
- 5
主要是应对代码合并压缩时,由于缺少分号;带来的错误。知道了上面的规则,在 ( 开头的行前加分号就可以避免错误了。

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
-
上一篇
一文读懂JDK1.7,JDK1.8,JDK1.9的hashmap,hashtable,concurrenthashmap及他们的区别
本篇为威力加强升级版本,读到最后,有惊吓 1:hashmap简介(如下,数组-链表形式) HashMap的存储结构 图中,紫色部分即代表哈希表,也称为哈希数组(默认数组大小是16,每对key-value键值对其实是存在map的内部类entry里的),数组的每个元素都是一个单链表的头节点,跟着的绿色链表是用来解决冲突的,如果不同的key映射到了数组的同一位置处,就将其放入单链表中。 2:hashmap原理(即put和get原理) 2.1 put原理 1.根据key获取对应hash值:int hash = hash(key.hash.hashcode()) 2.根据hash值和数组长度确定对应数组引int i = indexFor(hash, table.length); 简单理解就是i = hash值%模以 数组长度(其实是按位与运算)。如果不同的key都映射到了数组的同一位置处,就将其放入单链表中。且新来的是放在头节点。 2.2 get原理 1.通过hash获得对应数组位置,遍历该数组所在链表(key.equals()) 3:hashcode相同,冲突怎么办? “头插法”,放到对应的链...
-
下一篇
C++输入总结
总结下在编程题中的输入数据方法 #include <iostream> using namespace std; int main(){ int n; cin>>n; string s; // 注意,在VS中这里会报错,需要添加 #include<string> // 因为在iostream里,对string只是声明,并没有定义。 cin>>s; // 但是上面碰到空格会进行分段,如果想一次性输入整行; // 会截断回车符。 // 如果没有读入字符,将返回false; getline(cin,line); //如果想使用自定义分隔符 getline(cin,line,delime); while(getline(cin,tt,delime)){ process(tt); } // 注意 如果之前有cin,然后再进行getline之前,需要把cin没有处理的回车符处理掉, // 也就是在getline之前加个 cin.get(); // 如果想从一个字符串里读取数据 #include<sstream> string src("d...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- SpringBoot2整合MyBatis,连接MySql数据库做增删改查操作
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- MySQL数据库在高并发下的优化方案
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
- CentOS7,8上快速安装Gitea,搭建Git服务器
- CentOS6,7,8上安装Nginx,支持https2.0的开启
- CentOS8编译安装MySQL8.0.19
- Dcoker安装(在线仓库),最新的服务器搭配容器使用