--- tags: with,嚴謹模式,考古,廢棄 --- 考古:已經被柄棄的 with === 這是鮮少被使用的冷門語法,曾被認為很好用,但現下已被柄棄,甚至連 W3Schools 的 JavaScript 教材都沒有收錄此語法。甚至**在 E5 導入嚴謹模式時被禁止使用**。 以下簡單說明其使用方法,進一步講述其隱憂,並得出其被廢棄的原因,最後學習替代它的方法。 使用方法 --- 需要對同一物件的多個屬性或函式作操作時,可以使用 with 簡化程式碼。 - ### 使用於內建物件 ![](https://i.imgur.com/fUtCHCe.png) ```jsx let x = Math.cos(3 * Math.PI) + Math.sin(Math.LN10); let y = Math.tan(14 * Math.E); /* 兩者相同 */ let x,y; with(Math){ x = cos(3 * PI) + sin(LN10); y = tan(14 * E); } ``` - ### 使用於自訂物件 ![](https://i.imgur.com/gFDHPdF.png) ```jsx function playerDataFun(data){ console.log("Name: " + data.name); console.log("level: " + data.level); console.log("Exp: " + data.currentExp); console.log("You need more " + (data.nextLevelNeededExp - data.currentExp) + " Exp points for Level " + (data.level + 1) + "."); } /* 兩者相同 */ function playerDataFun(data){ with(data){ console.log("Name: " + name); console.log("level: " + level); console.log("Exp: " + currentExp); console.log("You need more " + (nextLevelNeededExp - currentExp) + " Exp points for Level " + (level + 1) + "."); } } ``` > [Dome](https://codepen.io/betty-hu/pen/WNKaWzo?editors=0012) 疑慮:撞名會發生什麼事? --- 分成兩種情況: - ### 變數在 with 區塊之「外」宣告 如果該屬性不存在於物件內,會按作用域鏈 (Scope Chain) 的順序,繼續尋找。 > [Dome](https://codepen.io/betty-hu/pen/bGjmPaZ?editors=0012) ![](https://i.imgur.com/KHQmKuj.png) #### 步驟: 1. 以預設物件的屬性為優先。 2. 按照作用域鏈 (Scope Chain)順序,往外尋找其他變數定義。 - ### 變數在 with 區塊之「內」宣告 以在 with 之內的宣告變數為優先。 > [Dome](https://codepen.io/betty-hu/pen/RwBezve?editors=0011) ![](https://i.imgur.com/NPkcYMF.png) - ### 變數在 with 區塊之外宣告,但在 with 區塊之內被使用 由於在 with 之內先找到變數,因此以 with 之內的變數優先。 > [Dome](https://codepen.io/betty-hu/pen/gOjBNJE) ![](https://i.imgur.com/GkBYGZ9.png) ### 總結 with 作用域的優先順序 遇到呼叫名稱時: 1. 先從 with 區塊內尋找,有使用過即可,不必是在區塊內宣告的變數。 2. 如果找不到,再從預設物件的屬性,尋找同名稱的屬性。 3. 如果再找不到,再根據作用域鏈 (Scope Chain) 繼續往外找。 摒棄 with 的原因 --- 使用 with 可以節省一點程式碼,但對於程式的作用域運作可能造成混亂。 從專案風險角度來看: 1. 能節省的程式碼量有限;再說,簡化程式碼屬於「盡量做到,但不能危害到程式運作」的加分項目。 2. 使用不慎,會造成程式運作的行為不同,屬於「一旦發生,會危害程式運作」的問題。 在權衡(trade-off)之下,毫無疑問第 2 點對專案的殺傷力遠大於第 1 點,使用 with 所得到的效益遠不及它隱含的風險,因此 with 的摒棄是可以被理解。 替代 with 的方法 --- 將需要重復呼叫的物件,暫存於一個名稱簡短的變數。 ![](https://i.imgur.com/29RB6gn.png) ```jsx let x = Math.cos(3 * Math.PI) + Math.sin(Math.LN10); let y = Math.tan(14 * Math.E); /* 兩者相同 */ let m = Math; let x = m.cos(3 * m.PI) + m.sin(m.LN10); let y = m.tan(14 * m.E); ``` > [Dome](https://codepen.io/betty-hu/pen/wvxQwjp)