---
# System prepended metadata

title: '"use strict" 嚴格模式'
tags: [JavaScript, 嚴格模式]

---

"use strict" 嚴格模式
===

use strict 新加入的標準
---
`'use strict'` 是 ES5 導入的標準，在不穩定的語法或妨礙最佳化的語意都會跳出警告，讓開發者避開這些寫法。

### 傳統瀏覽器如何解讀 `'use strict'`

在傳統瀏覽器（IE9 及以下版本）下 ，`'use strict'` 僅會被視為沒有用處的字串，所以不會對舊有的瀏覽器產生影響。

因為 `"use strict"` 本質是字串，不是關鍵字或描述句。

JavaScript 接受數字實字（numeric literal）或字串實字（string literal）單獨存在：

```jsx
3 + 4; // 數字實字 (numeric literal)
"Hello"; // 字串實字 (string literal)
```

這些值雖然產生，但沒有被存放在任何變數內，因此很快就被消滅，不會影響程式。所以不認得 `"use strict"` 的瀏覽器，也只會把它當成普通的字串實字，不會造成錯誤。

### 為何使用嚴謹模式

- 更容易寫出安全、不易出錯的 JavaScript 程式（"secure" JavaScript）。
- 幫助程式碼更乾淨。

### 建議使用方式

建議加入 `'use strict'`，可以改正編寫時的不良習慣，但也可以導致專案無法運作，此時可以考慮將 `'use strict'` 加在函式內，避免影響過去的程式碼及相關套件。

use strict 使用方法
---
宣告位置必須在**主程式或函數的開頭**，才會被辨識為嚴謹模式。

以「未定義的變數直接賦予值」的錯誤進行示範：

### 直接加在在程式碼前方
```jsx
'use strict';
auntie = '漂亮阿姨';
// Uncaught ReferenceError: auntie is not defined
```

#### 注意：只有將 ‘use strict’ 放在主程式開頭才會有作用。
![](https://i.imgur.com/oLMbZH7.png)

### 使用在函式的開頭，只會在此函式套用「嚴謹模式」，函式外部不受影響。
```jsx
(function () {
  'use strict';
  auntie = '漂亮阿姨';
  // Uncaught ReferenceError: auntie is not defined
})();
```

#### 注意：只有將 'use strict' 放在函式開頭才會有作用。
![](https://i.imgur.com/CQ6YG3H.png)

嚴格模式下不能做的事
---
### 不能使用未宣告的變數
> 錯誤類型：ReferenceError: x is not defined

```jsx
/* 一般 */

x = 3.14;
console.log(x); // 3.14
```
```jsx
/* 嚴謹模式*/
"use strict";

x = 3.14; // ReferenceError: x is not defined
console.log(x);
```
- 強化變數的控管，快速發現打錯變數名稱的 Bug。
- 避免不小心產生不必要的 Global 變數（賦值給尚未宣告的變數，JavaScript 預設行為產生 Global 變數）。

### 不能刪除變數或函數
> 錯誤類型：SyntaxError: Delete of an unqualified identifier in strict mode
- 不能對變數、函數使用 delete 運算子（原本對變數、函數作刪除並不會有效果，但也不會拋錯，等於無用的廢 code），仍可以對物件屬性、陣列元素使用。

```jsx
/* 一般 */

// 變數 variable（沒有作用）
var x = 3.14;
console.log( delete x );                    // false
console.log(x);                             // 3.14

// 運算函式 function via declararion（沒有作用）
function f1(){ console.log('Hi 1'); }
console.log( delete f1 );                   // false
console.log(f1);                            // function f1(){ console.log('Hi 1'); }

// 陳述函式 function via expression（沒有作用）
var f2 = function() { console.log('Hi 2'); }
console.log( delete f2 );                   // false
console.log(f2);                            // function () { console.log('Hi 2'); }

// 陣列 array element
var fruits = ["Banana", "Orange", "Apple", "Mango"];
console.log( delete fruits[2] );            // true
console.log(fruits);                        // ["Banana", "Orange", , "Mango"]

// 物件 object property
var person = {name: 'John', age:18};
console.log( delete person.name );          // true
console.log(person);                        // {age: 18}
```

```jsx
/* 嚴謹模式*/
"use strict";

// 變數 variable
var x = 3.14;
console.log( delete x );
// SyntaxError: Delete of an unqualified identifier in strict mode.

// 運算函式 function via declararion
function f1(){ console.log('Hi 1'); }
console.log( delete f1 );
// SyntaxError: Delete of an unqualified identifier in strict mode.

// 陳述函式 function via expression
var f2 = function() { console.log('Hi 2'); }
console.log( delete f2 );
// SyntaxError: Delete of an unqualified identifier in strict mode.

// 陣列 array element
var fruits = ["Banana", "Orange", "Apple", "Mango"];
console.log( delete fruits[2] ); // true
console.log(fruits); // ["Banana", "Orange", , "Mango"]

// 物件 object property
var person = {name: 'John', age:18};
console.log( delete person.name ); // true
console.log(person); // {age: 18}
```

### 不能對不可刪除的屬性（undeletable properties）使用 delete 運算子
> 錯誤類型：`TypeError: Cannot delete property 'prototype' of function Object() { [native code] }`

- 一般模式下，對不可刪除屬性使用 delete 運算子，delete 回傳 false，但語法仍可被接受。嚴謹模式下會直接拋錯。
- [可否刪除主要看 Property attributes](https://stackoverflow.com/questions/34357752/how-to-identify-javascript-undeletable-properties)，其在 configurable 屬性設置為 false 便不可刪除。若要查看 configurable 屬性，可以使用 [`Object.getOwnPropertyDescriptor(obj, prop)`](https://hackmd.io/PCdwS45ER2KWmh2BKQUS6g?view)。

```jsx
/* 一般 */

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

```jsx
/* 嚴謹模式 */
"use strict";

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

### 函數的參數名稱不能重複
> 錯誤類型：SyntaxError: Duplicate parameter name not allowed in this context
- 一般情況函數的參數名稱重複，不會有提醒，而是默認後面的蓋過前面。

```jsx
/* 一般 */

function myFunc(p1, p1){
    console.log(p1);
};

myFunc(10, 20); // 20
```

```jsx
/* 嚴謹模式 */
"use strict";

function myFunc(p1, p1){ 
// SyntaxError: Octal literals are not allowed in strict mode
    console.log(p1);
};
```

### 不能使用八進制的數字實字（Octal numeric literals）
> 錯誤類型：SyntaxError: Octal literals are not allowed in strict mode

- 某些版本的 JavaScript 中，如果數字前頭帶 0，會被直譯為八進制。例如：`var x = 010;` 等於十進制的 8。
- 一般 JavaScript 實作規範，就已經強烈建議不要對數字開頭帶 0，但僅是建議，一般模式下仍可被 JavaScript 直譯器接受。嚴謹模式則直接對這樣的語法拋錯。

```jsx
/* 一般 */

let n = 010;
console.log(n);　// 8
```

```jsx
/* 嚴謹模式 */
"use strict";

let n = 010;
// SyntaxError: Octal literals are not allowed in strict mode.
```

### 不能使用八進制的跳脫字元（Octal escape characters）
> 錯誤類型：SyntaxError: Delete of an unqualified identifier in strict mode.

- 例如：`var x = "\010";` 在一般模式下是空字串，但在嚴謹模式下被禁用。

```jsx
/* 一般 */

var n = "\010";
console.log(n); // ""
```

```jsx
/* 嚴謹模式 */
"use strict";

var n = "\010";
// SyntaxError: Octal escape sequences are not allowed in strict mode.
```

### 不能對唯讀物件屬性作寫入（write to a read-only property of objects）
> 錯誤類型：`TypeError: Cannot assign to read only property 'articleTarget' of object '#<Object>'`
    
- 定義物件屬性時，可以設定是否可寫入（writable）。詳見 [Object.defineProperty](https://hackmd.io/PCdwS45ER2KWmh2BKQUS6g?view)。
- 一般模式下，對唯讀屬性賦值，雖然不會寫入成功，但也不會報錯，等於是廢 code。
- 嚴謹模式下，對唯讀屬性賦值，會直接報錯。
    
```jsx
/* 一般 */

let player = {};

Object.defineProperty(player, "name", {
  value: "OneJar", 
  writable: false // 唯讀
});

player.name = "鮭魚"; // 寫入新屬性
console.log(player.name); // "OneJar"
```
> [Dome](https://codepen.io/betty-hu/pen/YzjRgpV?editors=0012)

```jsx
/* 嚴謹模式 */
"use strict";

let player = {};

Object.defineProperty(player, "name", {
  value: "OneJar", 
  writable: false // 唯讀
});

player.name = "鮭魚"; 
// TypeError: Cannot assign to read only property 'articleTarget' of object '#<Object>' 
```
> [Dome](https://codepen.io/betty-hu/pen/oNMQOqa?editors=0012)

### 不能對 get-only 的物件屬性作寫入
> 錯誤類型：`TypeError: Cannot set property age of #<Object> which has only a getter`
    
- 一般模式下，對只有 [getter](https://hackmd.io/AW0bOOXnT32DZV_uuvd5IQ?view) 的屬性賦值，雖然不會寫入成功，但也不會報錯，等於廢 code。嚴謹模式下，則會直接報錯。
    
```jsx
/* 一般 */

var person = { get age() {
  return 18
}};

console.log(person.age); // 18

person.age = 70;
console.log(person.age); // 18
```

```jsx
/* 嚴謹模式 */
"use strict";

var person = { get age() {
  return 18
}};

console.log(person.age); // 18

person.age = 70;  
// TypeError: Cannot set property age of #<Object> which has only a getter
```

### 不能使用 `eval` 或 `arguments` 作為變數名稱
> 錯誤類型：SyntaxError: Unexpected eval or arguments in strict mode

- `eval` 和 `arguments` 是 JavaScript 關鍵字，各自擁有用途，作為變數名稱容易導致非預期結果。
    - [arguments](https://hackmd.io/f0CWm4f3SgqP7HTNg-HerQ?view#arguments) 是  function 中所有的參數。
    - eval 將傳入的字符作為 JavaScript 執行。

```jsx
/* 一般 */

let arguments = 1500;
console.log(arguments); // 1500

function myFunc(){ return arguments; }
console.log(myFunc(1, 2, 3)); // Arguments Object [1, 2, 3]
```

```jsx
/* 嚴謹模式 */
"use strict";

let arguments = 1500; // SyntaxError: Unexpected eval or arguments in strict mode 
console.log(arguments);

function myFunc(){ return arguments;}
console.log(myFunc(1, 2, 3));
```

### 不能使用未來的保留字做變數名稱（cannot use future reserved keywords as variables）
> 錯誤類型：`TypeError: Cannot assign to read only property 'articleTarget' of object '#<Object>'` 

- 有些字眼目前不是 JavaScript 關鍵字，但根據程式語言發展的經驗，被預期在未來成為實際作用的關鍵字。例如 interface、public、private、package 等，在其他程式語言是很重要也很普遍的關鍵字。
- 考慮到既有程式對未來 JavaScript 新版本的移植性（portable），這些字眼就不適合作為變數名稱。

以下是 W3Schools 列出的未來保留字，有些在 ES6 等後續版本已經成真：
- implements
- interface
- let
- package
- private
- protected
- public
- static
- yield

### 用 `eval()` 宣告的變數或函數，不能在該 Scope 以外被語法呼叫使用
> 錯誤類型：ReferenceError: x is not defined

- 一般模式下，可以利用 [eval](https://hackmd.io/WlGpP3e6SCCOD_jeFep6uw?view) 宣告變數或函數，但是在嚴謹模式下，eval 內宣告的變數或函數在外使用時，將會發現找不到。

```jsx
/* 一般 */

/* 變數 */
eval("var x = 123");
console.log(x); // 123

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

```jsx
/* 嚴謹模式 */
"use strict";

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

/* 函數 */
eval("function myFunc(){ var x = 123; console.log(x); } myFunc();"); // 123
myFunc(); // ReferenceError: myFunc is not defined 
```

- 在嚴謹模式下，僅在 eval 使用自己宣告的變數或函數，是沒有問題的。
```jsx
/* 嚴謹模式 */
"use strict";

var ret = 0;
eval("var n1 = 3; ret = n1 + n1;");

console.log(ret); // 6
console.log(n1); // ReferenceError: n1 is not defined
```

### 不能使用 [with](https://hackmd.io/jK-e8TJSQ1eCLWmEOuwzlQ?view) 語法
> 錯誤類型：SyntaxError: Strict mode code may not include a with statement

```jsx
/* 嚴謹模式 */
"use strict";

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

use strict 及 this
---
在 `'use strict'` 的環境下，純粹調用（Simple call）的 this 不再是全域變數。

![](https://i.imgur.com/9mNalyW.png)

▲ 原本在非嚴格模式，Simple call 的 `this` 可以是全域變數。

![](https://i.imgur.com/NyZgDZB.png)

▲ 在嚴格模式時，Simple call 的 `this` 卻是 undefined。

因為沒有 `this`，因此可以使用 [call](https://hackmd.io/C_r0ziSJScuw51ZogCLmJg#call) 的方式繫結 `this`。

![](https://i.imgur.com/MiHLsVq.png)

▲ 自己定義要繫結的 `this`。

![](https://i.imgur.com/DepgXtR.png)

▲ 直接繫結 `this`，以 window 的方式傳入。

明明是不良語法，卻尚未被嚴謹模式控管：重複宣告變數
---
```jsx
/* 嚴謹模式 */
"use strict";

var x = 10;
var x = 20;
console.log(x); // 20
```
▲ 重複宣告變數是不良語法之一，卻尚未被嚴謹模式規範，需要自行注意。

```jsx
let x = 10;
let x = 20; // SyntaxError: Identifier 'x' has already been declared 
```
▲ 可以使用 let 或 const 取代 var，限制變數重複宣告。