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(1 < 10) a = 1
console.log(a)
// 等价于
if(1 < 10) a = 1;
console.log(a);

2. 在continue,return,break,throw后自动插入分号

return
{a: 1}
// 等价于
return;
{a: 1};

     3. ++、--后缀表达式作为新行的开始,在行首自动插入分号

a
++
c
// 等价于
a;
++c;

     4. 代码块的最后一个语句会自动插入分号

function(){ a = 1 }
// 等价于
function(){ a = 1; }

   No ASI的规则

 1. 新行以 ( 开始

var a = 1
var b = a
(a+b).toString()
// 会被解析为以a+b为入参调用函数a,然后调用函数返回值的toString函数
var a = 1
var b =a(a+b).toString()

     2. 新行以 [ 开始

var a = [‘a1‘, ‘a2‘]
var b = a
[0,1].slice(1)
// 会被解析先获取a[1],然后调用a[1].slice(1)。
// 由于逗号位于[]内,且不被解析为数组字面量,而被解析为运算符,而逗号运算符会先执行左侧表达式,然后执行右侧表达式并且以右侧表达式的计算结果作为返回值
var a = [‘a1‘, ‘a2‘]
var b = a[0,1].slice(1)

  3. 新行以 / 开始

var a = 1
var b = a
/test/.test(b)
// /会被解析为整除运算符,而不是正则表达式字面量的起始符号。浏览器中会报test前多了个.号
var a = 1
var b = a / test / .test(b)

4.   新行以 + 、 - 、 % 和 * 开始

var a = 2
var b = a
+a
// 会解析如下格式
var a = 2
var b = a + a

   5.  新行以 , 或 . 开始

var a = 2
var b = a
.toString()
console.log(typeof b)
// 会解析为
var a = 2
var b = a.toString()
console.log(typeof b)

到这里我们已经对ASI的规则有一定的了解了,另外还有一样有趣的事情,就是“空语句”。

// 三个空语句
;;;

// 只有if条件语句,语句块为空语句。
// 可实现unless条件语句的效果
if(1>2);else
  console.log(‘2 is greater than 1 always!‘);

// 只有while条件语句,循环体为空语句。
var a = 1
while(++a < 100);

三、前置分号                          

重申一下分号的作用——作为语句的断言(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 = 0; idx = isObj ? keys[i] : i, i < len; ++i)
         fn.call(ctx, idx, arrayLike[idx])
    }
}(new Function(‘return this‘)(), void 0))

forEach({‘s‘:1,‘c‘:2}, function(i, item){
  console.log(i + ‘  ‘ + item)
})
forEach([1,2], 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

时间: 2024-10-19 20:53:48

JS魔法堂:ASI(自动分号插入机制)和前置分号的相关文章

JS魔法堂:LINK元素深入详解

一.前言 我们一般使用方式为 <link type="text/css" rel="stylesheet" href="text.css"> 来引入外部层叠式样式文件,但LINK元素各属性的具体含义.资源加载行为等方面却了解不多,本文打算稍微深入一下. 由于内容较多,特设目录一坨: 二.到底有没有结束标签? 三.普通属性介绍 四.属性disabled详解 1. Attribute和Property的disabled  2. disab

JS魔法堂:元素克隆、剪切技术研究

原文:JS魔法堂:元素克隆.剪切技术研究 一.前言 当需要新元素时我们可以通过 document.createElement 接口来创建一个全新的元素,也可以通过克隆已有元素的方式来获取一个新元素.而在部分浏览器中,通过复制来获取新元素的效率比通过 document.createElement 方式的要高一些,具体的性能比较如下: 2% in IE8, but no change in IE6 and IE7 Up to 5.5% in Firefox 3.5 and Safari 4 6% i

JS魔法堂:IMG元素加载行为详解

一.前言 在<JS魔法堂:jsDeferred源码剖析>中我们了解到img元素加载失败可以作为函数异步执行的优化方案,本文打算对img元素的加载行为进行更深入的探讨. 二.资源加载的相关属性和事件 资源加载首先当然是确定资源位置的 src属性 .随之就是资源加载成功与否的 onload事件 和 onerror事件 ,对于IE5~10来说还多了一个 onreadystatechage事件 和与该事件相关联的 readyState属性 和 complete属性 . onload事件 ,当资源加载完

JS魔法堂:属性、特性,傻傻分不清楚

一.前言 或许你和我一样都曾经被下面的代码所困扰 var el = document.getElementById('dummy'); el.hello = "test"; console.log(el.getAttribute('hello')); // IE67下输出test,其他浏览器输出null “搞毛啊?”,苦逼的Jser对着浏览器大呼一声.然后就用下面蹩脚的方式草草处理掉了. function getAttr(el, prop){ return el[prop] || el

JS魔法堂:浏览器模式和文档模式怎么玩?

一.前言 从IE8开始引入了文档兼容模式的概念,作为开发人员的我们可以在开发人员工具中通过“浏览器模式”和“文档模式”(IE11开始改为“浏览器模式”改成更贴切的“用户代理字符串”)品味一番,它的出现极大地方便了苦逼的前端攻城狮们适配各版本的IE,但jser们也不能完全信任它,因为它只是提供尽可能的文档模式模拟而已. 本篇大部分内容来源于官方解说:http://msdn.microsoft.com/library/cc288325(v=vs.85).aspx,并尽量融入个人平常工作中踩过的坑加以

JS魔法堂:jsDeferred源码剖析

一.前言 最近在研究Promises/A+规范及实现,而Promise/A+规范的制定则很大程度地参考了由日本geek cho45发起的jsDeferred项目(<JavaScript框架设计>提供该资讯,再次感谢),追本溯源地了解jsDeferred是十分有必要的,并且当你看过官网(http://cho45.stfuawsc.com/jsdeferred/)的新手引导后就会有种不好好学学就太可惜的感觉了,而只看API和使用指南是无法满足我对它的好奇心的,通过解读源码读透它的设计思想才是根本.

JS魔法堂:精确判断IE的文档模式by特征嗅探

一.前言 苦逼的前端攻城狮都深受浏览器兼容之苦,再完成每一项功能前都要左顾右盼,生怕浏览器不支持某个API,生怕原生API内含臭虫因此判断浏览器类型和版本号成了不可绕过的一道关卡,而特征嗅探是继浏览器探测后另一利器处理上述问题. 二.何为特征嗅探 从前我们都是通过对navigator.userAgent或navigator.appName两个属性值进行特定字符串匹配和萃取来区分浏览器类型和获取版本号的.但随着IE8提供可选的文档兼容性模式设置和各种加壳浏览器的出现,导致无法通过navigator

JS魔法堂:追忆那些原始的选择器

一.前言                                                                                                    首先这里说的原始选择器是指除 querySelector . querySelectorAll 外的其他选择器.从前我只使用 getElementById 获取元素并没有觉得有什么问题,但随着参与项目的前端规模逐步扩大,踩的坑就越来越多,于是将踩过的和学习过的经验教训记录在这里,供以后好查阅

JS魔法堂:doctype我们应该了解的基础知识

一.前言 什么是doctype?其实我们一直使用,却很少停下来看清楚它到底是什么,对网页有什么作用.本篇将和大家一起探讨那个默默无闻的doctype吧! 二.什么是doctype doctype或DTD就是声明在文档首行,位于<html>前,用于告知浏览器该文档遵循那种级别的HTML或XHTML规范. 其声明格式如下: <!DOCTYPE① html② PUBLIC③ "公共标识符"④ "系统标识符"⑤> ①. dotype固定的起始部分 ②