JavaScript中的嚴格模式

將拼寫錯轉成異常

這個我的理解是,對於變數不能不宣告就直接使用,在非嚴格模式下,就相當於無意建立了一個全域性變數,在嚴格模式下是不允許的,這樣做會報Uncaught ReferenceError

"use strict";

testing = true;
//Uncaught ReferenceError: testing is not defined at <anonymous>:2:9

靜默失敗的賦值操丟擲異常

對沒有寫許可權的屬性、對只讀的屬性、對不可擴充套件的物件的新屬性進行賦值操作均會報Uncaught TypeError

"use strict";

NaN = 0;
//Uncaught TypeError: Cannot assign to read only property 'NaN' of object '#<Window>'

var testObj1 = Object.defineProperty({}, "x", {value: "xTest", wirtable: false});
testObj1.x = "xxTest";
//Uncaught TypeError: Cannot assign to read only property 'x' of object '#<Object>'

var testObj2 = {get y(){return "yTest";}}
testObj2.y = "yyTest";
//Uncaught TypeError: Cannot set property y of #<Object> which has only a getter

var testObj3 = {};
Object.preventExtensions(testObj3);
testObj3.x = "zTest";
//Uncaught TypeError: Cannot add property x, object is not extensible

試圖刪除不可刪除的屬性

在非嚴格模式下,刪除不可刪除的屬性時會返回false,而在嚴格模式下會報Uncaught TypeError型別的錯誤

"use strict";

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

物件的屬性名不能重複

在兩種模式下,都自動修復了物件

"use strict";

var o = { p: 1, p: 2 };
console.log(o)
//{p: 2}

函式的引數名唯一

這個說的是,函式的形參的名字不能夠有重複的,經過測試發現,在較新的瀏覽器下,嚴格模式和非嚴格模式的表現是一致的都會報Uncaught SyntaxError: Duplicate parameter name not allowed in this context錯誤,但是在IE7-9版本下沒有報錯

"use strict";

function sum(a, a, c){
  "use strict";
  return a + b + c;
}
//Uncaught SyntaxError: Duplicate parameter name not allowed in this context

禁止八進位制數字語法

非嚴格模式下,所有的瀏覽器都支援以零(0)開頭的八進位制語法

consle.log(0644 === 420)
//true

嚴格模式下,比較奇怪

IE7-9

"use strict"

consle.log(0644 === 420)
//true

consle.log(0o644 === 420)
//報錯:缺少 ')'

IE10-11

"use strict"

consle.log(0644 === 420)
//strict 模式下不允許使用八進位制數字引數和轉義字元

consle.log(0o644 === 420)
//報錯:缺少 ')'

chrome(63) / firefox(57)

"use strict"

consle.log(0644 === 420)
//Uncaught SyntaxError: Octal literals are not allowed in strict mode.

consle.log(0o644 === 420)
//true

禁止設定primitive型別變數的屬性

嚴格模式下,禁止給原子型別的變數設定屬性,賦值

IE7-11

(function() {
    "use strict";

    false.true = "";              //TypeError
    (14).sailing = "home";        //TypeError
    "with".you = "far away";      //TypeError
})();
//沒有報錯,但是設定的屬性都沒有用

chrome(63) / firefox(57)

(function() {
    "use strict";

    false.true = "";              //TypeError
    (14).sailing = "home";        //TypeError
    "with".you = "far away";      //TypeError
})();
//chrome:Uncaught TypeError: Cannot create property 'true' on boolean 'false'
//firefox: TypeError: can't assign to properties of (new Boolean(false)): not an object

嚴格模式禁用 with

IE7-9:不管在什麼模式下,都會輸出1

"use strict"

var x = 17;
var obj = {x: 1}
with (obj){
    console.log(x)
}
//1

IE10-11

"use strict"

var x = 17;
var obj = {x: 1}
with (obj){
    console.log(x)
    // 如果沒有開啟嚴格模式,with中的這個x會指向obj.x
}
//strict 模式下不允許使用“with”語句

chrome(63) / firefox(57)

"use strict"

var x = 17;
var obj = {x: 1}
with (obj){
    console.log(x)
    // 如果沒有開啟嚴格模式,with中的這個x會指向obj.x
}
//chrome:Uncaught TypeError: Strict mode code may not include a with statement
//firefox: SyntaxError: strict mode code may not contain 'with' statements

禁止刪除宣告變數

IE7-9:不管在什麼模式下,都會成功的刪除x,而且console.log(x)還會報錯”x未定義”

"use strict"

var x = 1;
delete x;

IE10-11

"use strict"

var x = 1;
delete x;
//strict 模式下不允許對錶達式呼叫 Delete

chrome(63) / firefox(57) (非嚴格模式下,刪除都會失敗)

"use strict"

var x = 1;
delete x;
//chrome:Uncaught SyntaxError: Delete of an unqualified identifier in strict mode.
//firefox: SyntaxError: applying the 'delete' operator to an unqualified name is deprecated

嚴格模式下的 eval 不再為上層範圍(surrounding scope,注:包圍eval程式碼塊的範圍)引入新變數

一般情況下, 在一個包含 eval 呼叫的函式內所有沒有引用到引數或者區域性變數的名稱都必須在執行時才能被對映到特定的定義 (因為 eval 可能引入的新變數會覆蓋它的外層變數). 在嚴格模式下 eval 僅僅為被執行的程式碼建立變數, 所以 eval 不會使得名稱對映到外部變數或者其他區域性變數

對於這個,我的理解是,在非嚴格模式下,執行到了eval裡的程式碼,裡面的變數才會影響到eval後面的程式碼裡的重名變數的值(值覆蓋),而在eval之前的程式碼裡的重名變數是不會受到影響的,在嚴格模式下,eval裡面的變數和外面的重名變數是互不影響的

非嚴格模式的測試程式碼:

var x = 17;
console.log(x === 17);
var evalX = eval(" var x = 42; console.log(x === 17);console.log(x === 42);x");
console.log(x === 17);
console.log(x === 42);
console.log(evalX === 42);

IE7-9

日誌: true 
日誌: false 
日誌: true 
日誌: false 
日誌: true 
日誌: true

chrome(63)

VM81:2 true
VM82:1 false
VM82:1 true
VM81:4 false
VM81:5 true
VM81:6 true

非嚴格模式下,IE7-9和chrome的表現是一樣,eval函式內部的變數值覆蓋了外面的重名的變數

嚴格模式的測試程式碼:

var x = 17;
console.log(x === 17);
var evalX = eval("'use strict'; var x = 42; console.log(x === 17);console.log(x === 42);x");
console.log(x === 17);
console.log(x === 42);
console.log(evalX === 42);

IE7-9

日誌: true 
日誌: false 
日誌: true 
日誌: false 
日誌: true 
日誌: true

chrome(63)

VM77:3 true
VM78:1 false
VM78:1 true
VM77:5 true
VM77:6 false
VM77:7 true

嚴格模式下,IE7-9的表現和它在非嚴格模式下是一樣(沒有覆蓋了eval後面的變數),chrome下eval函式內部的變數值沒有覆蓋外面重名的變數

嚴格模式下將eval和arguments作為關鍵字

測試程式碼:

"use strict";
eval = 17;
arguments++;
++eval;
var obj = { set p(arguments) { } };
var eval;
try { } catch (arguments) { }
function x(eval) { }
function arguments() { }
var y = function eval() { };
var f = new Function("arguments", "'use strict'; return 17;");

非嚴格模式下,這些程式碼在IE7-9和chrome下都不會報錯
在嚴格模式下,IE7-9也沒有報錯,但是在chrome下,會報Uncaught SyntaxError: Unexpected eval or arguments in strict mode錯誤

嚴格模式下,引數的值不會隨形參的值的改變而變化

嚴格模式下,函式的 arguments 物件會儲存函式被呼叫時的原始引數。arguments[i] 的值不會隨與之相應的引數的值的改變而變化,同名引數的值也不會隨與之相應的 arguments[i] 的值的改變而變化。

測試程式碼:

function f(a){
  a = 42;
  console.log(arguments);
  console.log(arguments[0]);
  ++arguments[0];
  console.log(a);
  console.log(arguments[0])
  return [a, arguments[0]];
}
var pair = f(17);
console.log(pair[0] === 42);
console.log(pair[1] === 17);

IE7-9

日誌: [object Arguments] {
    0 : 42
} 
日誌: 42 
日誌: 43 
日誌: 43 
日誌: false 
日誌: false

chrome

VM132:3 [42, callee: ƒ, Symbol(Symbol.iterator): ƒ]0: 43callee: ƒ f(a)length: 1Symbol(Symbol.iterator): ƒ values()arguments: (...)caller: (...)length: 0name: "values"__proto__: ƒ ()__proto__: Object
VM132:4 42
VM132:6 43
VM132:7 43
VM132:11 false
VM132:12 false

非嚴格模式下,IE7-9和chrome的表現大體相同,除了一點,輸出的arguments的第一個引數的值不同,在chrome下值發生了變化,而IE7-9下值還是形參的值(賦值後的)
在嚴格模式下,IE7-9的表現和非嚴格模式下一樣,chrome在嚴格模式下,形參和arguments互不影響,而且第一個輸出的arguments的第一個形參值也變化了

VM308:4 [17, callee: (...), Symbol(Symbol.iterator): ƒ]0: 18callee: (...)length: 1Symbol(Symbol.iterator): ƒ values()get callee: ƒ ()set callee: ƒ ()__proto__: Object
VM308:5 17
VM308:7 42
VM308:8 18
VM308:12 true
VM308:13 false

不再支援 arguments.callee

在嚴格模式下,arguments.callee 是一個不可刪除屬性,而且賦值和讀取時都會丟擲異常

測試程式碼:

"use strict";
var f = function() { return arguments.callee; };
f(); 

IE7-9在兩種模式下都不會報錯,chrome在非嚴格模式下也不會報錯,在嚴格模式下會報Uncaught TypeError: ‘caller’, ‘callee’, and ‘arguments’ properties may not be accessed on strict mode functions or the arguments objects for calls to them

“安全的” JavaScript

第一,在嚴格模式下通過this傳遞給一個函式的值不會被強制轉換為一個物件。
對於一個開啟嚴格模式的函式,指定的this不再被封裝為物件,而且如果沒有指定this的話它值是undefined

一段有趣的程式碼:

"use strict";
function fun() { return this; }
console.assert(fun() === undefined);
console.assert(fun.call(2) === 2);
console.assert(fun.apply(null) === null);
console.assert(fun.call(undefined) === undefined);
console.assert(fun.bind(true)() === true);