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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM