iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 5
2
Modern Web

前端常見問題攻略系列 第 5

Day5 JS 加不加分號差在哪?

  • 分享至 

  • xImage
  •  

這個議題許多論壇、文件都有討論,在 ESlint 的規範中也可從加不加分號區分為兩大派系,至於加不加分號關鍵點則需要了解分號對於 JavaScript 的影響,開始之前可以先看看以下題目。

下方程式碼的運行預期為:

  1. 宣告 a 的值為 1
  2. 執行立即函式
  3. 於 console 中呈現 2

請問這段程式碼運行是否正確?

var a = 1
(function() {
  console.log(2)
})()

.
.
.
.
如果你嘗試運行這段程式碼,則會出現此錯誤:

Uncaught TypeError: 1 is not a function

什麼? 1 is not a function ?我們沒有打算運行數字 1,為何要說數字 1 不是函式,這種錯誤很難查找到原因,常常會在錯誤的行數上打轉。這個錯誤主要原因是上述程式碼運行時被視為同一行,概念如下:

var a = 1(function() { /* */ })()

因此立即函式的 () 附加在 1 上,這是一個呼叫函式的語法,所以會造成 1 is not a function 的錯誤,如果要避免此錯誤就需要使用分號將其隔開:

var a = 1 // 分號隔在哪都好,隔開就對了
;(function() {
  console.log(2)
})()

ASI 自動加入分號

ASI 是 「Automatic Semicolon Insertion」的縮寫,部分的斷行程式碼運行時會自動插入分號,這個機制可以部分程式碼如果沒有加入分號也能正確運行,如以下範例:

var b = 1
++b
console.log('b', b)

此範例中,由於 ++ 是屬於一元表達式,它在僅能在表達式的左方右方置入運算元,如果沒有 ASI 的機制程式碼會轉為「var b = 1 ++ b」 的錯誤結構。不過好在有 ASI,實際運行時他會被加入分號如下,也就不會出現如上的錯誤。

var b = 1;
++b;
console.log('b', b); // 2

return 與分號的關係

再來看一個有感的片段,以下程式碼中在 return 的後方空一行後再回傳值,請問運行結果是什麼呢?

function fn() {
  return 
  '小明'
}
console.log(fn())

這段程式碼因為 ASI 的修正,return 後方會被加上一個分號,也因為如此 return 與預期回傳的值被分了開來,所以 return 的內容為空值,最終函式也僅能接收到 undefined 的結果。

function fn() {
  return;
  '小明';
}
console.log(fn()); // undefined

所以,分號到底要怎麼處理呢?

ASI 是出自於善意,用來修正沒有加入分號的片段程式碼,但偏偏有些地方沒有發揮其用途(如本篇一開始所介紹到的立即函式),導致程式碼出現了錯誤;甚至有些程式碼不會出錯,但會讓你的程式碼運行結果不如預期。

要解決 ASI 的問題,解決方式如下:

  • 無論如何都加入分號,自行決定程式碼的分段
  • 謹記「不會」自動加入分號的規則,當不會自動插入分號時則手動加入

不會自動加入分號的規則

以下整理各種「不會」自動加入分號的規則:

  1. 新的一行是 ([/ 開始,這類型通常會直接出現「Uncaught TypeError」導致程式碼無法運行(其後方的程式碼也會無法運作)。
var a = 1
var b = a
(a + b).toString()

var a = 1
[1,2,3].forEach(bar)
 
(function() { })()
(function() { })()
 
var a = 1
var b = a
/test/.test(b)
  1. 新的一行以 +-*% 作開始,這類型大多會影響運算結果,建議合併為一行撰寫。
var a = 2
var b = a
+a
  1. 新的一行以 ,. 作開始,這類型在實戰中很常會運用,主要是避免程式碼過長所加入的分段,並不會影響運行,多加善用可使程式碼更易於閱讀。
var a = 2
var b = a
  .toString()
console.log(typeof b)
 
var a = 1
,b = 2 // b 一樣會 var 被宣告

因此,遇到以上的標點符號前方加入分號也是解決辦法

如果需要加入分號的狀況,除了在語句的末端加入分號外,也可將分號改在「不會自動加入分號」的最前方,如 () 本身不會自動加入分號,當有此需求時可以將 ; 加入至前方(ESLint Standard JS 規範就選擇此方法避免錯誤)。

// 執行錯誤
(function() { })()
(function() { })()
 
// 正確
;(function() { })()
;(function() { })()

結語

不加入分號可以讓程式碼看起來更乾淨、精簡(對於習慣加入分號的開發者不這麼認為就是了),且在大部分正確的分段下並不會出現錯誤,僅有少部分的語句會出現執行上的錯誤(很少,大多都可避免),因此許多開發者也會選擇不加入分號,而我過去也是屬於這類型規範的愛好者。

不過在教學上,我會更傾向選擇嚴格的規範提供範例給學員,所以近幾年開發都轉為選用加入分號的規則。至於選擇何種,只要清楚運行上的限制,只要團隊允許都是好的方法。


上一篇
開發總是沒問題,上線老是各種錯 - JS 莫名其妙的地方
下一篇
JS 物件名詞解釋及常見觀念問題
系列文
前端常見問題攻略30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言