Day 14:來挖挖恐龍骨 —— with 語法
Last updated
Was this helpful?
Last updated
Was this helpful?
JavaScript 有一個語法 with
似乎相對冷門,比較少看到被使用。
事實上連 都沒有 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
語法的使用。
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: )
建議: