JS魔法堂:ASI(自动分号插入机制)和前置分号
一、前言
今晚在知乎看到百姓网前端技术专家——贺师俊对《JavaScript 语句后应该加分号么?》的回答,让我又一次看到大牛的风采,实在佩服万分。但单纯的敬佩是不足以回报他如此优秀的文字,必须深入理解文字的含义和背后的原理才不愧呢!
在这之前我们需要先理解ASI(自动分号插入机制)。
二、 Automatic Semicolon Insertion (ASI, 自动分号插入机制)
主要参考:http://justjavac.com/javascript/2013/04/22/automatic-semicolon-insertion-in-javascript.html
从事C#和Java的猴子们都知道分号是用作断句(EOS,end of statement)的,而且必须加分号,否则编译就不通过了。但JavaScript由于存在ASI机制,因此允许我们省略分号。ASI机制不是说在解析过程中解析器自动把分号添加到代码中,而是说解析器除了分号还会以换行为基础按一定的规则作为断句的依据,从而保证解析的正确性。
首先这些规则是基于两点:
1. 以换行为基础;
2. 解析器会尽量将新行并入当前行,当且仅当符合ASI规则时才会将新行视为独立的语句。
ASI的规则
1. 新行并入当前行将构成非法语句,自动插入分号
if( < ) a =
console.log(a)
// 等价于
if( < ) a = ;
console.log(a);
2. 在continue,return,break,throw后自动插入分号
return
{a: }
// 等价于
return;
{a: };
3. ++、--后缀表达式作为新行的开始,在行首自动插入分号
a
++
c
// 等价于
a;
++c;
4. 代码块的最后一个语句会自动插入分号
function(){ a = }
// 等价于
function(){ a = ; }
No ASI的规则
1. 新行以 ( 开始
var a =
var b = a
(a+b).toString()
// 会被解析为以a+b为入参调用函数a,然后调用函数返回值的toString函数
var a =
var b =a(a+b).toString()
2. 新行以 [ 开始
var a = ['a1', 'a2']
var b = a
[,].slice()
// 会被解析先获取a[1],然后调用a[1].slice(1)。
// 由于逗号位于[]内,且不被解析为数组字面量,而被解析为运算符,而逗号运算符会先执行左侧表达式,然后执行右侧表达式并且以右侧表达式的计算结果作为返回值
var a = ['a1', 'a2']
var b = a[,].slice()
3. 新行以 / 开始
var a =
var b = a
/test/.test(b)
// /会被解析为整除运算符,而不是正则表达式字面量的起始符号。浏览器中会报test前多了个.号
var a =
var b = a / test / .test(b)
4. 新行以 + 、 - 、 % 和 * 开始
var a =
var b = a
+a
// 会解析如下格式
var a =
var b = a + a
5. 新行以 , 或 . 开始
var a =
var b = a
.toString()
console.log(typeof b)
// 会解析为
var a =
var b = a.toString()
console.log(typeof b)
到这里我们已经对ASI的规则有一定的了解了,另外还有一样有趣的事情,就是“空语句”。
// 三个空语句
;;; // 只有if条件语句,语句块为空语句。
// 可实现unless条件语句的效果
if(>);else
console.log('2 is greater than 1 always!'); // 只有while条件语句,循环体为空语句。
var a =
while(++a < );
三、前置分号
重申一下分号的作用——作为语句的断言(EOS),目的是让解析器正确解析程序。那既然存在ASI机制,那为什么还有那么多团队的代码规范中还规定必须写分号呢?不外乎三个原因:1. 因为存在No ASI的情况,懒得记忆这些特例;2. 团队的工程师需要兼顾前后端开发(苦逼如我~~),而后端采用Java、C#或PHP,保持两端代码规范接近管理成本较低;3. 旧有的规范就是这样,现在也没必要改了。
对于省略分号后代码压缩工具会出问题,jslint会对无分号的代码报warning等问题,贺师俊已经在回复中对其进行详细说明了。因此分不分号纯属个人和团队的偏好问题,当然也可以混合使用咯(下面借一下大牛@高原的图)
对于我这种能少敲键盘则少敲,能不用鼠标就不用的大懒虫,自然而然加入到“无分号党”的怀抱咯,入党的前提条件就是记住一下规则来应付No ASI的情况:
在以 ([/+- 开头的语句前加分号(由于正常写法均不会出现以 .,*% 作为语句开头,因此只需记住前面5个即可,你看能懒则懒哦)
然后就是通过合理的缩进和空白行来使代码结构更为清晰(coffeescript不就是这样的吗?!)
示例:
;(function(exports, undefined){
var getKeys = Object.getOwnPropertyName
&& Object.getOwnPropertyName.bind(Object)
|| function(obj){
var keys = []
for (var key in obj)
keys.push(key)
return keys
} var each = exports.forEach = exports.each = function(arrayLike, fn, ctx){
if(arrayLike == undefined) return var isObj = arrayLike.length !== +arrayLike.length
,keys = isObj ? getKeys(arrayLike) : arrayLike
,len = keys.length
,idx
for (var i = ; idx = isObj ? keys[i] : i, i < len; ++i)
fn.call(ctx, idx, arrayLike[idx])
}
}(new Function('return this')(), void )) forEach({'s':,'c':}, function(i, item){
console.log(i + ' ' + item)
})
forEach([,], function(i, item){
console.log(i + ' ' + item)
})
现在我们就可以安心做“无分号党”了哦!
四、总结
ASI再一次展示JavaScript语法的自由度之高,因此对于团队开发而言代码规范显得如此的重要。而对语法的掌握程度也从另一个侧面反映前端工程师的技术水平。看来要继续努力才行了!
尊重原创,转载请注明来自:http://www.cnblogs.com/fsjohnhuang/p/4154503.html ^_^肥子John
五、其他参考
http://justjavac.com/javascript/2013/04/22/automatic-semicolon-insertion-in-javascript.html
http://inimino.org/~inimino/blog/javascript_semicolons
http://blog.izs.me/post/2353458699/an-open-letter-to-javascript-leaders-regarding
JS魔法堂:ASI(自动分号插入机制)和前置分号的更多相关文章
- JS魔法堂:不完全国际化&本地化手册 之 理論篇
前言 最近加入到新项目组负责前端技术预研和选型,其中涉及到一个熟悉又陌生的需求--国际化&本地化.熟悉的是之前的项目也玩过,陌生的是之前的实现仅仅停留在"有"的阶段而已. ...
- JS魔法堂:不完全国际化&本地化手册 之 实战篇
前言 最近加入到新项目组负责前端技术预研和选型,其中涉及到一个熟悉又陌生的需求--国际化&本地化.熟悉的是之前的项目也玩过,陌生的是之前的实现仅仅停留在"有"的阶段而已. ...
- JavaScript中的分号插入机制
原文:JavaScript中的分号插入机制 仅在}之前.一个或多个换行之后和程序输入的结尾被插入 也就是说你只能在一行.一个代码块和一段程序结束的地方省略分号. 也就是说你可以写如下代码 functi ...
- JS魔法堂:LINK元素深入详解
一.前言 我们一般使用方式为 <link type="text/css" rel="stylesheet" href="text.css&quo ...
- JS魔法堂:IMG元素加载行为详解
一.前言 在<JS魔法堂:jsDeferred源码剖析>中我们了解到img元素加载失败可以作为函数异步执行的优化方案,本文打算对img元素的加载行为进行更深入的探讨. 二.资源加载的相关属 ...
- JS魔法堂:jsDeferred源码剖析
一.前言 最近在研究Promises/A+规范及实现,而Promise/A+规范的制定则很大程度地参考了由日本geek cho45发起的jsDeferred项目(<JavaScript框架设计& ...
- JS魔法堂:属性、特性,傻傻分不清楚
一.前言 或许你和我一样都曾经被下面的代码所困扰 var el = document.getElementById('dummy'); el.hello = "test"; con ...
- JS魔法堂:那些困扰你的DOM集合类型
一.前言 大家先看看下面的js,猜猜结果会怎样吧! 可选答案: ①. 获取id属性值为id的节点元素 ②. 抛namedItem is undefined的异常 var nodes = documen ...
- JS魔法堂:浏览器模式和文档模式怎么玩?
一.前言 从IE8开始引入了文档兼容模式的概念,作为开发人员的我们可以在开发人员工具中通过“浏览器模式”和“文档模式”(IE11开始改为“浏览器模式”改成更贴切的“用户代理字符串”)品味一番,它的出现 ...
随机推荐
- Yii2 使用Composer
composer 是 PHP 用来管理依赖(dependency)关系的工具.你可以在自己的项目中声明所依赖的外部工具库(libraries),Composer 会帮你安装这些依赖的库文件 compo ...
- ASP.NET MVC 学习笔记(一)
很久很久没有在博客园写过东西了,很多大虾也说过展示自己最好的地方就是有一个博客作为笔记,展示一下自己的学习和研究成果. 最近决心将公司的一款产品改用MVC的方式实现,于是乎就开始在园子里面疯狂的寻找各 ...
- 网页集成paypal支付
在网站中集成paypal支付有两种方式: 1.通过paypal账户的按钮创建工具 进入paypal 商户账号,选择创建按钮工具,有包括添加到购物车.购买.租用三类按钮. 之后会生成一段代码,直接将代码 ...
- Hadoop日记Day12---MapReduce学习
一.MapReduce简介 1.1MapReduce概述 MapReduce是一种分布式计算模型,由Google提出,主要用于搜索领域,解决海量数据的计算问题.MR由两个阶段组成:Map和Reduce ...
- ZooKeeper学习第一期---Zookeeper简单介绍
一.分布式协调技术 在给大家介绍ZooKeeper之前先来给大家介绍一种技术——分布式协调技术.那么什么是分布式协调技术?那么我来告诉大家,其实分布式协调技术主要用来解决分布式环境当中多个进程之间的同 ...
- 【读书笔记】javascript 继承
在JavaScript中继承不像C#那么直接,C#中子类继承父类之后马上获得了父类的属性和方法,但JavaScript需要分步进行. 让Brid 继承 Animal,并扩展自己fly的方法. func ...
- Linux 比较判断运算(if test)
200 ? "200px" : this.width)!important;} --> 介绍 本篇文章主要是列举在shell命令中常出现的一些用来做比较的运算符,这些运算符是 ...
- windows 8.1 试用感受:蛋疼感大幅降低
众所周知windows 8 的最大使用感受就是蛋疼. 无论是微软MVP,还是我这样的万年不悔微软小白鼠,普通用户,小白用户,或多或少的都对这款操作系统感到蛋疼. 槽点太多,以至于大家都懒得批判了.好在 ...
- Git bash 配置ssh key
问题描述 昨天为了配置Qt create中的Git,把我一直在使用的Github删除了,今本以为,这样git的一些配置还在,可是,今天上传一些提交的时候,提示我,git没有密钥.梳理一下,这个简单的配 ...
- Android 在View中更新View
直接用Invalidate()方法会导致错误:只有主线程才能更新UI 取而代之的是可以使用postInvalidate(); 原因: 最终会调用ViewRootImpl类的dispatchInvali ...