JS專題之嚴格模式
ECMAScript 5 引入了 strict mode ,現在已經被大多瀏覽器實現(從IE10開始)
一、什麼是嚴格模式
顧名思義,JavaScript 嚴格模式就是讓 JS 程式碼以更嚴格的模式執行,不允許可能會引發錯誤的程式碼執行。在正常模式下靜默失敗的程式碼,嚴格模式下就會丟擲錯誤。
二、為什麼要過渡到嚴格模式
- 嚴格模式下的程式碼在執行的時候,更容易通過丟擲的錯誤定位到問題所在的地方
- 嚴格模式能夠幫助你編寫更符合規範的程式碼
- 消除 JavaScript 語言上一些不合理,比較怪異的行為
- 為未來新版本的 JavaScript 做鋪墊
- 有時候,嚴格模式下的 JavaScript 程式碼執行起來更快
三、如何使用
· 指令碼檔案範圍
將"use strict";
放在指令碼檔案的第一行。整個指令碼檔案就會以“嚴格模式”執行。
· 函式作用域範圍
將"use strict";
放在函式體的第一行,則整個函式以"嚴格模式"執行。
檔案合併時,寫在指令碼檔案第一行的"use strict";
來實現嚴格模式會失效,可以將指令碼檔案的程式碼放在一個立即執行表示式中。
(funciton() { "use strict"; ... })()
四、嚴格模式的具體定義
- 嚴格模式下無法再隱式建立全域性變數
也就是,變數必須聲明後才能使用,正常模式直接賦值給一個未定義的變數時,會將變數定義為全域性變數。
"use strict"; var a = b = 3;// Uncaught ReferenceError: b is not defined 以上程式碼等於: var a; b = 3; a = b;
-
禁止
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
- 不允許在函式內部遍歷呼叫棧
禁止使用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()
- 禁止向物件的只讀屬性賦值,禁止刪除物件的不可設定屬性, 禁止向不可擴充套件的物件新增屬性
無法刪除 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
- 物件不允許有重名的屬性,函式不允許有重名的引數
在正常模式中,物件的重名屬性,位置靠後會覆蓋位置靠前的重名屬性。函式也是,函式體查詢到的引數,靠後的重名引數會覆蓋靠前的重名引數。
"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 並不會報錯,會採用覆蓋機制。
- 靜態繫結
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 內部使用。
- 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]
- ES5禁止在非函式程式碼塊宣告函式
ES5 的嚴格模式只允許在全域性作用域或函式作用域宣告函式。也就是說,不允許在非函式的程式碼塊內宣告函式。
if (true) { function add() { } } add() for (var i = 0; i < 5; i++){ function f2() { } // !!! 語法錯誤 f2(); } 以上程式碼在嚴格模式是禁止的,但是在 ES6 中,是允許在程式碼塊中宣告函式的。
- 保留關鍵字
嚴格模式中一部分字元變成了保留的關鍵字。這些字元包括implements, interface, let, package, private, protected, public, static
和yield
。在嚴格模式下,你不能再用這些名字作為變數名或者形參名
function private() {"use strict" }//Uncaught SyntaxError: Unexpected strict mode reserved word
- 嚴格模式禁止八進位制數字語法
五、向嚴格模式過渡
嚴格模式能夠幫助我們寫出更安全,更有規範的程式碼,則應該避免一些危險的寫法,採用更好的寫法:
- 變數先宣告,再使用,
- this 應該在指向自己建立的物件時使用。
- arguments 應該在函式第一行就拷貝出來。
六、嚴格模式的缺點
- 現在的程式碼都會進行檔案壓縮和合並,此時嚴格模式就會失效。
總結
現在的 webpack 會在打包的時候預設是嚴格模式,所以現在不用再手動寫use strict
了。嚴格模式能幫助我們以更規範的方式書寫程式碼,但是無論是否嚴格模式,都應該注意程式碼的規範,避免隱式 bug 的出現。
2018/02/08@Starbucks