---
# System prepended metadata

title: 考古：已經被柄棄的 with
tags: [考古, with, 廢棄, 嚴謹模式]

---

---
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)