JS專題之嚴格模式

ECMAScript 5 引入了 strict mode ,現在已經被大多瀏覽器實現(從IE10開始)

一、什麼是嚴格模式

顧名思義,JavaScript 嚴格模式就是讓 JS 代碼以更嚴格的模式執行,不允許可能會引發錯誤的代碼執行。在正常模式下靜默失敗的代碼,嚴格模式下就會拋出錯誤。

二、爲什麼要過渡到嚴格模式

  1. 嚴格模式下的代碼在運行的時候,更容易通過拋出的錯誤定位到問題所在的地方
  2. 嚴格模式能夠幫助你編寫更符合規範的代碼
  3. 消除 JavaScript 語言上一些不合理,比較怪異的行爲
  4. 爲未來新版本的 JavaScript 做鋪墊
  5. 有時候,嚴格模式下的 JavaScript 代碼運行起來更快

三、如何使用

· 腳本文件範圍

"use strict"; 放在腳本文件的第一行。整個腳本文件就會以“嚴格模式”執行。

· 函數作用域範圍

"use strict"; 放在函數體的第一行,則整個函數以"嚴格模式"運行。

文件合併時,寫在腳本文件第一行的 "use strict"; 來實現嚴格模式會失效,可以將腳本文件的代碼放在一個立即執行表達式中。

(funciton() {
    "use strict";
    ...
})()

四、嚴格模式的具體定義

  1. 嚴格模式下無法再隱式創建全局變量

也就是,變量必須聲明後才能使用,正常模式直接賦值給一個未定義的變量時,會將變量定義爲全局變量。

"use strict";
var a = b = 3;  // Uncaught ReferenceError: b is not defined

以上代碼等於:
var a;
b = 3;
a = b;
  1. 禁止 this 關鍵字指向全局對象

正常模式下,函數中如果沒有指明 this 對象,JS 則會將 this 隱式指向爲全局對象。如果綁定的值是非對象,將被自動轉爲對象再綁定上去,而 null 和 undefined 這兩個無法轉成對象的值,將被忽略。

嚴格模式下,必須指明 this 的指向對象。如果沒有指明的話,this的值爲 undefined

var name = "foo";
function func() {
    "use strict";
    this.name;  // Uncaught TypeError: Cannot read property 'name' of undefined
}
func();  // 沒有加 new 關鍵字
new func();

function func() {
    return this
}

func() // window
func.call(8) // Number {8}
func.call(true) // Boolean {true}
func.call("abcd")  // {"abcd"}
func.call(null) // window
func.call(undefined) // window

"use strict"
function func() {
    return this
}

func() // undefined
func.call(8) // 8
func.call(true) // true
func.call(null) //null
func.call(undefined) // undefined
  1. 不允許在函數內部遍歷調用棧

禁止使用 arguments.callee、arguments.caller、fn.caller、fn.callee;
在嚴格模式下,arguments.callee 是一個不可刪除屬性,而且賦值和讀取時都會拋出異常

function func() {
    "use strict";
    func.caller;  // 報錯
    func.arguments; // Uncaught TypeError: 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them
}
func()
  1. 禁止向對象的只讀屬性賦值,禁止刪除對象的不可設置屬性, 禁止向不可擴展的對象添加屬性

無法刪除 var 聲明的變量。

在正常模式中,給對象的只讀屬性賦值, 刪除對象的不可設置屬性,添加不可擴展對象的新屬性,會靜默失敗。

但是在嚴格模式中,會拋出錯誤。
另外,字符串的屬性 length 也是隻讀屬性,修改後會報錯。

"use strict";
var str = "abc"
str.length = 8  // Uncaught TypeError: Cannot assign to read only property 'length' of string 'abc'

'use strict';
var obj = Object.defineProperty({}, 'a', {
  value: 37,
  writable: false
});
obj.a = 123; // Uncaught TypeError: Cannot assign to read only property 'a' of object '#

'use strict';
var obj = Object.defineProperty({}, 'p', {
  value: 37,
  configurable: false
});
delete obj.p  // Uncaught TypeError: Cannot delete property 'p' of #<Object>

var obj = {};
Object.preventExtensions(obj);
obj.title = "hello";  // Uncaught TypeError: Cannot add property title, object is not extensible
  1. 對象不允許有重名的屬性,函數不允許有重名的參數

在正常模式中,對象的重名屬性,位置靠後會覆蓋位置靠前的重名屬性。函數也是,函數體查找到的參數,靠後的重名參數會覆蓋靠前的重名參數。

"use strict";
var o = { 
    p: 1,
    p: 2
   };
// IE報錯:strict 模式下不允許一個屬性有多個定義, 新版的 Chrome 和 firefox 並不會報錯,會採用覆蓋機制。

"use strict";
function func(a, a) {
    console.log(a)
}
func(1, 2) // IE報錯: strict 模式下不允許正式參數名稱重複。新版的 Chrome 和 firefox 並不會報錯,會採用覆蓋機制。
  1. 靜態綁定

JavaScript 支持動態綁定,也就是 JavaScript 的屬性和方法是在運行時確定,而不是在編譯時確定。
於是,JavaScript 嚴格模式禁用了 with 語句, 因爲使用了 with 語句,with 語句塊中變量無法確定是外部全局變量還是傳入的對象屬性。

"use strict";
var x = 17;
with (obj) // !!! 語法錯誤
{
  // 如果沒有開啓嚴格模式,with 中的這個x會指向 with 上面的那個 x,還是obj.x?
  // 如果不運行代碼,我們無法知道,因此,這種代碼讓引擎無法進行優化,速度也就會變慢。
  x; // Uncaught SyntaxError: Strict mode code may not include a with statement
}

eval 關鍵字不再會給上層函數(surrounding function)或者全局引入一個新的變量。在嚴格模式中,eval 語句會創建自己的一個作用域,eval 裏的變量只能在 eval 內部使用。

  1. arguments 的限定

嚴格模式規定名稱爲 eval 和 arguments 不能通過程序語法被綁定(be bound)或賦值
嚴格模式下,參數的值不會隨 arguments 對象的值的改變而變化。

正常模式中,對參數重新賦值,會修改 arguments 類數組對象下的參數值。同時,修改 arguments 類數組對象的值,也會修改函數參數的值。

嚴格模式下,不僅參數的值不會隨着 arguments 類數組對象的變化而變化,參數的變化也不會引起 arguments 對象的變化,arguments 對象會記住參數的傳入初始值。

function func(a) {
"use strict"
  a = 8;
  // arguments[0] = 8
  return [a, arguments[0]]
}

func(3) // [8, 3]

function func(a) {
"use strict"
  arguments[0] = 8
  return [a, arguments[0]]
}

func(3) // [3, 8]
  1. ES5禁止在非函數代碼塊聲明函數

ES5 的嚴格模式只允許在全局作用域或函數作用域聲明函數。也就是說,不允許在非函數的代碼塊內聲明函數。

if (true) {
  function add() {
  }
}
add()

for (var i = 0; i < 5; i++){
  function f2() { } // !!! 語法錯誤
  f2();
}

以上代碼在嚴格模式是禁止的,但是在 ES6 中,是允許在代碼塊中聲明函數的。
  1. 保留關鍵字

嚴格模式中一部分字符變成了保留的關鍵字。這些字符包括implements, interface, let, package, private, protected, public, staticyield。在嚴格模式下,你不能再用這些名字作爲變量名或者形參名

function private() {"use strict" }  //Uncaught SyntaxError: Unexpected strict mode reserved word
  1. 嚴格模式禁止八進制數字語法

五、向嚴格模式過渡

嚴格模式能夠幫助我們寫出更安全,更有規範的代碼,則應該避免一些危險的寫法,採用更好的寫法:

  1. 變量先聲明,再使用,
  2. this 應該在指向自己創建的對象時使用。
  3. arguments 應該在函數第一行就拷貝出來。

六、嚴格模式的缺點

  1. 現在的代碼都會進行文件壓縮和合並,此時嚴格模式就會失效。

總結

現在的 webpack 會在打包的時候默認是嚴格模式,所以現在不用再手動寫 use strict了。嚴格模式能幫助我們以更規範的方式書寫代碼,但是無論是否嚴格模式,都應該注意代碼的規範,避免隱式 bug 的出現。

2018/02/08 @Starbucks
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章