# Day 13：看 Strict Mode 如何施展「還我漂亮拳」(2)

## 嚴謹模式下你不能做的事 (續)

### 8. 不能對不可刪除的屬性 (undeletable properties) 使用 `delete` 運算子

* 一般模式下，`delete` 會回傳 `false`，但語法本身仍可以被接受。
* 嚴謹模式下會直接拋錯。

> 錯誤類型：`TypeError: Cannot delete property 'prototype' of function Object() { [native code] }`

使用前：

```javascript
console.log( delete Object.prototype ); // false
```

使用後：

```javascript
"use strict";

console.log( delete Object.prototype ); // TypeError: Cannot delete property 'prototype' of function Object() { [native code] }
```

### 9. 不能使用 `eval` 或 `arguments` 作為變數名稱

* `eval` 和 `arguments` 是 JavaScript 的關鍵字，各自另有用途，拿來作變數名稱容易導致非預期的結果。

> 錯誤類型：`SyntaxError: Unexpected eval or arguments in strict mode`

使用前：

```javascript
function myFunc(){
    return arguments;
}

var arguments = 1500;
console.log(arguments);         // 1500
console.log(myFunc(1, 2, 3));   // Arguments Object [1, 2, 3]
```

使用後：

```javascript
"use strict";

function myFunc(){
    return arguments;
}

var arguments = 1500;           // SyntaxError: Unexpected eval or arguments in strict mode
console.log(arguments);
console.log(myFunc(1, 2, 3));
```

### 10. 不能使用未來的保留字做變數名稱 (cannot use future reserved keywords as variables)

* 有些字眼可能還不是當前 JavaScript 版本支援的關鍵字，但根據程式語言發展的經驗，有些字眼被預期很有機會在未來成為實際有作用的關鍵字。
* 例如 `interface`、`public`、`private`、`package` 等，在其他程式語言都是很重要也很普遍的關鍵字。
* 考慮到既有程式對未來 JavaScript 新版本的移植性 (portable)，這些字眼就不適合用來作為變數名稱。
* 以下是 W3Schools 列出的未來保留字，有些在 ES6 等後續版本已經成真：
  * implements
  * interface
  * let
  * package
  * private
  * protected
  * public
  * static
  * yield

> 錯誤類型：`TypeError: Cannot assign to read only property 'articleTarget' of object '#<Object>'`

使用前：

```javascript
var public = 1500;
console.log(public);    // 1500
```

使用後：

```javascript
"use strict";
var public = 1500;      // SyntaxError: Unexpected strict mode reserved word
console.log(public);
```

### 11. 用 `eval()` 宣告的變數或函數，不能在該 Scope 被語法呼叫使用

* W3Schools 的原文是：For security reasons, eval() is not allowed to create variables in the scope from which it was called.
* 但嚴格來說，並不是 `eval()` 宣告的時候拋錯，而是後續想使用的時候拋錯，而且拋出的錯誤類型是 `ReferenceError`。
* 此外，如果是在 `eval()` 內自己宣告自己使用也沒有問題，後續想用語法去呼叫使用才會拋錯。
* 所以這邊我的理解是：
  * **在嚴謹模式下，`eval()` 所執行的語法會自成一個暫時的 Scope**，在裡面宣告的變數或函數都只屬於這個 Scope。
  * 所以後續用語法呼叫時，不認得該變數或函數，因而拋出 `ReferenceError` 類型的錯誤。

> 錯誤類型：`ReferenceError: x is not defined`

使用前(用於變數)：

```javascript
eval("var x = 123");
console.log(x);         // 123
```

使用後(用於變數)：

```javascript
"use strict";

eval("var x = 123");
console.log(x);         // ReferenceError: x is not defined
```

嚴謹模式下，在 `eval()` 內自己宣告自己使用並沒有問題：

```javascript
"use strict";

var ret = 0;
eval("var n1 = 3, n2 = 6; ret = n1 + n2;");
console.log(ret);   // 9
console.log(n1);    // ReferenceError: n1 is not defined
```

使用前(用於函數)：

```javascript
eval("function myFunc(){ var x = 123; console.log(x); } myFunc();");
myFunc();
```

```
123
123
```

使用後(用於函數)：

```javascript
"use strict";

eval("function myFunc(){ var x = 123; console.log(x); } myFunc();");
myFunc();
```

```
123
ReferenceError: myFunc is not defined
```

### 12. 不能使用 `with` 語法

* 完全禁止 `with` 語法。

> 錯誤類型：`SyntaxError: Strict mode code may not include a with statement`

使用前：

```javascript
var x, y;
with (Math){
    x = cos(3 * PI) + sin(LN10);
    y = tan(14 * E);
};
console.log(x); // -0.2560196630425069
console.log(y); // 0.37279230719931067
```

使用後：

```javascript
"use strict";

var x, y;
with (Math){   // SyntaxError: Strict mode code may not include a with statement
    x = cos(3 * PI) + sin(LN10);
    y = tan(14 * E);
};
console.log(x);
console.log(y);
```

### 13. 全域執行環境內的函數，裡面的 `this` 代表的物件不一樣

* 一般模式下，全域執行環境內的函數裡面的 `this`，指的是 Global Object。
* 嚴謹模式下，全域執行環境內的函數裡面的 `this`，變成 `undefined`。

使用前：

```javascript
function myFunc(){
    console.log(this === window); // true
}
myFunc();
```

使用後：

```javascript
"use strict";
function myFunc(){
    console.log(this === window); // false (`this` is `undefined`)
}
myFunc();
```

## 嚴謹模式下你還是能……

![](https://i.imgur.com/21mdrMB.jpg)\
(Source: [網路](https://puui.qpic.cn/qqvideo_ori/0/l07381q86of_496_280/0))

嚴謹模式下增加了很多規範，目的是讓程式更安全。

不過也不是盡善盡美，還是有遺珠。

### 1. 還是可以重複宣告變數

* 這是一個很基本的不良語法，不過嚴謹模式並沒有加以控管。

```javascript
"use strict";

var x = 10;
var x = 20;
console.log(x);     // 20
```

這部分會建議使用前面文章介紹過的 `let` 或 `const` 來取代 `var`，就可以限制變數重複宣告。

## 總結

嚴謹模式的使用情境相信還有很多，無法在幾篇文章的篇幅內全數囊括。但希望透過這三篇文章的介紹，能對嚴謹模式有一定深度的了解。

當專案規模越來越大，嚴謹的開發模式和程式撰寫方式才能讓專案更容易被多人維護，使用嚴謹模式是個必然的趨勢。

事實上可以注意到，著名的 JavaScript 編譯器 Babel 在將新版 ECMAScript 轉譯成 ES5 時，在你還沒打任何程式碼之前，ES5 的那一欄已經預設好一行程式，就是 `use strict`！

![](https://i.imgur.com/3B5o5VH.png)\
(Source: [Babel REPL](https://babeljs.io/repl))

## References

* [W3Schools - JavaScript Use Strict](https://www.w3schools.com/js/js_strict.asp)
* [Strict mode - JavaScript | MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode)
* [with - JavaScript | MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/with)
* [【转载】JavaScript中with、this用法小结](https://king39461.pixnet.net/blog/post/361793370-%E3%80%90%E8%BD%AC%E8%BD%BD%E3%80%91javascript%E4%B8%ADwith%E3%80%81this%E7%94%A8%E6%B3%95%E5%B0%8F%E7%BB%93)
* [\[JavaScript\] with的用法](https://stannotes.blogspot.com/2014/10/javascript-with.html)
* [Try it out - Babel · The compiler for next generation JavaScript](https://babeljs.io/repl)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://something-about-js-book.onejar99.com/day13.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
