Day 14:來挖挖恐龍骨 —— with 語法
Last updated
Last updated
JavaScript 有一個語法 with
似乎相對冷門,比較少看到被使用。
事實上連 W3Schools 的 JavaScript 教材 都沒有 with
語法教學!查了一下網路討論,with
曾經也是有人認為很好用的語法。那麼究竟發生什麼事,讓 with
像個黑歷史一樣,被 W3Schools 刻意遺忘?
Day13 介紹嚴謹模式 (Strict Mode) 的例子時提到,with
語法甚至已經在 ES5 導入嚴謹模式後被禁止使用。
那本篇去研究一個已經被淘汰的語法有什麼意義?
對,語法沒意義,你知道了也不能用 (毆)。
(Source: 豆卡頻道貼圖)
這一篇文章的定位確實有點像考古文,瞭解一下 JavaScript 曾有過這個語法。
但想要探討的不是這個語法怎麼寫,而是它為什麼會被摒棄?
(Source: 網路圖片)
一個語法或特性被特地發展出來,然後又被淘汰,一定有其缺點或原因。
瞭解這些缺點,相當於學到什麼是較被建議的程式思維。這種概念性上的收穫就像內功一樣,可能不會直接轉化成某種語法的顯性應用,但有助於隱性的程式撰寫思維。
當然,要想探討 with
的缺點,首先要先知道語法怎麼寫。
with
的語法with
語法可以為一段程式敘述指定預設物件,用來簡化特定情形下必須撰寫的程式碼量。
語法模板如下:
以上是 with
標準的語法說明。
如果光看這種論文式的說明就知道怎麼用,你一定是百年一見的練武奇才。
沒學過如來神掌的人,請跟著導遊繼續往下看實際舉例。
with
使用於內建物件的範例下面是一個使用 JavaScript 內建數學運算物件 Math
的例子:
可以發現 Math
被不斷重複呼叫,使得這一段程式碼看起來很累贅。
這時候可以利用 with
,讓程式碼變得較簡潔易讀:
可以看到,在 with(Math){....}
區塊內,不用再逐一指定每個函式或屬性的呼叫物件,因為已經在 with
的小括號內指定了 Math
作為預設的呼叫物件。
簡單來說,當你需要對同一個物件的多個屬性或函式作操作時,就可以使用 with
來簡化你的程式碼。
除了用在內建的 JavaScript 物件,也可以用在自定義的物件上嗎?
當然可以。
with
使用於自訂物件的範例以下想對自訂物件 player
的多個屬性作操作,印出想要的資訊:
執行結果:
可以看到 showHeroStatus()
的內容有點囉嗦,不斷重複對 hero
的呼叫。
可以利用 with
語法讓這段程式碼更簡潔:
看到這邊,應該可以隱約感覺到 with
語法的疑慮:如果我另外有同名的變數會發生什麼事?
with
區塊之外宣告執行結果:
在 with
之內會以預設物件的屬性為優先。
如果該屬性名稱不存在於物件內,會按照作用域鏈 (Scope Chain)的順序,繼續找其他變數定義。
在這個例子裡:
level
會以 hero.level
優先。
hero
沒有 money
這個屬性,所以 money
會找到 Function Scope 所宣告的 money
變數。
with
區塊之內宣告執行結果:
會以在 with
之內的宣告變數為優先。
在這個例子裡:
呼叫 level
時,會以區塊內的 var level = 99
優先。
with
區塊之外宣告,但在 with
區塊之內被使用執行結果:
雖然 level
宣告在 with
區塊之外,而 hero
有 hero.level
這個屬性名稱,但在 with
之內還是先找到變數 level
。
with
作用域的優先順序當遇到一個呼叫名稱時 (例如 level
, money
): 1. 先從 with
區塊內尋找,有使用過即可,不必是在區塊內宣告的變數。 2. 如果找不到,再從預設物件 (例如 hero
) 的屬性去找同名稱的屬性。 3. 如果以上都找不到,再根據一般的作用域鏈 (Scope Chain) 繼續往外找。
with
的風險從上面的例子應該可以體會到,使用 with
固然可以節省一點程式碼,但對於程式的作用域運作可能造成混亂。
例如上面的例子 1 和 3,level
都是宣告在 with
之外,卻因為是否曾經在 with
之內被使用而有不同行為,這對於程式維護安全性來說並非好事。
尤其從現實專案風險的角度來看: 1. 使用 with
能節省的程式碼量可能有限;而且簡化程式碼通常屬於「能做到最好,但不能危害到程式運作」的加分項目。 2. 如果使用 with
不慎,造成程式運作的行為不同,屬於「一旦發生,會危害到程式運作」的問題。
專案最大的課題就是權衡 (trade-off)。從以上兩點來看,毫無疑問第 2 點對專案的殺傷力遠大於第 1 點,使用 with
所得到的效益可能遠不及它隱含的風險,因此 with
的摒棄是可以被理解。
with
掰掰~如前面文章介紹到,在 ES5 導入的嚴謹模式已經禁止 with
語法的使用。
MDN 建議:
Using with is not recommended, and is forbidden in ECMAScript 5 strict mode. The recommended alternative is to assign the object whose properties you want to access to a temporary variable.
如果單純希望程式碼可以再簡潔一點,MDN 的建議是「將需要重複呼叫的物件暫存於一個名稱簡短的變數」就好。
例如下面這樣:
(Source: 網路圖片)