前端JavaScript '嚴格模式' 的詳細總結

在閱讀jQuery源碼的時候,發現jQuery用到的是'use strict'嚴格模式。所以就去查閱了一些資料來補充本身對嚴格模式的認識。下面是參考【javascript高級程序設計(第三版)】的內容。javascript

1.嚴格模式(strict mode)

1.1 什麼是嚴格模式

  ‘嚴格模式’最先引入是ECMAScript5。經過嚴格模式,能夠在函數內部選擇進行較爲嚴格的全局或局部的錯誤條件檢測。

html

【補充】ECMAScript各版本的劃分,推薦:阮一峯老師中ECMAScript的歷史介紹java

1.2 使用嚴格模式的好處

能夠提前知道代碼中存在的錯誤,及早捕獲一些可能致使編程錯誤的ECMAScript行爲。es6

1.3 支持嚴格模式的瀏覽器

理解嚴格模式的規則很是重要,支持嚴格模式的瀏覽器有web

  • IE 10+
  • Firefox 4+
  • Safari 5.1+
  • Chrome

2. 如何使用嚴格模式

當咱們想要讓本身編寫的代碼處在嚴格模式的時候,可使用嚴格模式的編譯指示(pragma),也就是一個不會賦給任何變量的字符串:'use strict'這就話須要放在想要處於嚴格模式的首句。編程

這種語法能夠向後兼容那些不支持嚴格模式的javascript引擎。瀏覽器

  • 支持嚴格模式的引擎會啓動這種模式
  • 不支持該模式的引擎就當遇到一個未賦值的字符串字面量,會忽略這個編譯指示

若是在全局做用域中給出的這個編譯指示,則整個腳本都將使用嚴格模式。也就是這個編譯指示放在哪裏,只要有javascript代碼,就會讓其處於嚴格模式。安全

在函數中使用嚴格模式。把'use strict'放在首位,就能夠對整個函數其做用app

function fn(){
  'use strict'
  //要實現的代碼
}
複製代碼

【補充】字面量: 百度百科的解釋
通俗理解:let a = 1;這個等號左邊的叫作變量,等號右邊的就叫作字面量編輯器

3. 頁面中使用嚴格模式與不使用嚴格模式的區別

3.1 變量

在嚴格模式下,何時建立變量以及怎麼建立變量都是有限制的。

3.1.1 不容許意外建立全局變量

未聲明變量在嚴格模式非嚴格模式下的區別:

  • 非嚴格模式:即便 message前面沒有 var關鍵字,也沒有定義爲全局對象的屬性;也能夠將它建立爲全局變量
  • 嚴格模式 :若是給一個沒有聲明的變量賦值或者變量名拼寫錯誤。那代碼在執行的時候就會拋出 ReferenceError這個錯誤
message = 'Hello World!'
console.log(window.message)  //'Hello World!'
複製代碼
'use strict'
message = 'Hello World!'
console.log(window.message);  //Uncaught ReferenceError: message2 is not defined
複製代碼

3.1.2 不能對變量調用delete操做符

  • 非嚴格模式:容許使用 delete刪除元素,但會靜態默認失敗(也就是刪除不了,可是不會報錯,哈哈)
  • 嚴格模式:刪除變量也會致使錯誤
var color = 'red';
delete color;
console.log(color); //'red'
複製代碼
'use strict'
var color = 'red';
delete color;
console.log(color); //Uncaught SyntaxError: Delete of an unqualified identifier in strict mode. 
複製代碼

3.1.3 對變量名的限制

不能使用 implementsinterfaceletpackageprivateprotectedpublicstaticyield 做爲變量名。這些都是保留字,未來的 ECMAScript 版本中可能會用到它們。

  • 非嚴格模式:使用上面的關鍵字不會報錯,可是最好不要用(變量的命名規範中有一條就是不能使用JS中的關鍵字和保留字)
  • 嚴格模式下:用以上標識符做爲變量名會致使語法錯誤。
var implements=1;
console.log(implements); //1
複製代碼
'use strict'
var implements=1;
console.log(implements);//Uncaught SyntaxError: Unexpected strict mode reserved word
複製代碼

3.2 對象

在嚴格模式下操做對象比在非嚴格模式下更容易致使錯誤。

  • 非嚴格模式下會靜默失敗的情形
  • 在嚴格模式下就會拋出錯誤

所以,在開發中使用嚴格模式會加大早發現錯誤的可能性。

3.2.1 在下列情形下操做對象的屬性會致使錯誤:

  • 爲只讀屬性賦值會拋出 TypeError
  • 對不可配置的(nonconfigurable)的屬性使用 delete 操做符會拋出 TypeError
  • 爲不可擴展的(nonextensible)的對象添加屬性會拋出 TypeError
"use strict";

// 給不可寫屬性賦值
var obj1 = {};
Object.defineProperty(obj1, "x", { value42writablefalse });
obj1.x = 9// 拋出TypeError錯誤

// 給只讀屬性賦值
var obj2 = { get x() { return 17; } };
obj2.x = 5// 拋出TypeError錯誤

// 給不可擴展對象的新屬性賦值
var fixed = {};
Object.preventExtensions(fixed);
fixed.newProp = "ohai"// 拋出TypeError錯誤
複製代碼

3.2.2 使用對象的另外一個限制與經過對象字面量聲明對象有關。在使用對象字面量時,屬性名必須惟一。

這個問題在ECMAScript6中已經不復存在,也就是使用嚴格模式時,和非嚴格模式同樣,都是最後那個值,不會報錯

  • 非嚴格模式:沒有錯誤,以第二個屬性爲準
  • 嚴格模式:拋出語法錯誤
"use strict";
var person = { 
  name"Nicholas"
  name"Greg" 
}; 
複製代碼

這裏的對象 person 有兩個屬性,都叫 name。在非嚴格模式下,person 對象的 name 屬性值是第二個,而在嚴格模式下,這樣的代碼會致使語法錯誤。

3.3 函數

3.3.1 命名函數的參數必須惟一

  • 非嚴格模式:沒有錯誤,只能訪問第二個參數。經過參數名只能訪問第二個參數,要想訪問第一個就須要經過arguments對象
  • 嚴格模式:拋出語法錯誤
function sum(num,num){
  //函數體
  return num + num; // 要想訪問第一個參數:return arguments[0] + num;
}
console.log(sum(10,20)); // 40
複製代碼
'use strict';
function sum(num,num){
  //函數體
  return num + num;
}
console.log(sum(10,20)); // Uncaught SyntaxError: Duplicate parameter name not allowed in this context
複製代碼

3.3.2 修改命名空間參數的值與arguments的映射關係

  • 非嚴格模式:修改會反映到 arguments
  • 嚴格模式:修改不會反映到 arguments
function sum(num){
   num=20;
   console.log(arguments[0]); //20
   console.log(num);  //20
}
sum(10);
複製代碼
'use strict';
function sum(num){
   num=20;
   console.log(arguments[0]); //10
   console.log(num);  //20
}
sum(10);
複製代碼

  以上代碼中,函數sum()只有一個命名參數 num。調用這個函數時傳入了一個參數10, 這個值賦給了num。而在函數內部,num被改成20。在非嚴格模式下,這個修改也會改變 arguments[0]的值,但在嚴格模式下,arguments[0]的值仍然是傳入的值。

3.3.3 淘汰了 arguments.callee 和 arguments.caller

  • 非嚴格模式下:這兩個屬性一個引用函數自己,一個引用調用函數。
  • 嚴格模式下:訪問哪一個屬性都會拋出 TypeError。
function factorial(num{
   if (num <= 1) {
      return 1;
   } else {
      return num * arguments.callee(num - 1)
   }
}
var result = factorial(5);
console.log(result); //120 =>5*4*3*2*1
複製代碼
'use strict';
function factorial(num{
   if (num <= 1) {
      return 1;
   } else {
      return num * arguments.callee(num - 1)
   }
}
var result = factorial(5);
console.log(result); //120 =>5*4*3*2*1
複製代碼

【補充】arguments.callee是已經被廢棄的,屬性用在函數執行的時候調用自身(能夠理解爲遞歸的思想)。雖然arguments.caller 已經不可以使用了,可是你還可使用 Function.caller

3.3.4 函數名的限制

與變量相似,嚴格模式對函數名也作出了限制,不容許用 implementsinterfaceletpackageprivateprotectedpublicstaticyield 做爲函數名。

3.3.5 在 if / for 語句中聲明函數會致使語法錯誤:

  • 非嚴格模式:將函數提高到 if / for 語句外部
  • 嚴格模式:拋出語法錯誤
if (true) {
   function f(console.log(1); }
}
f(); //函數執行輸出1

for (var i = 0; i < 5; i++) {
   function f2({console.log(1); }
   f2();  //輸出5次1
}
f2();  //輸出1
複製代碼
"use strict";
if (true) {
   function f(console.log(1); }
}
f(); //Uncaught ReferenceError: f is not defined  at

for (var i = 0; i < 5; i++) {
   function f2({console.log(1); }
   f2();  //輸出5次1
}
f2();  //Uncaught ReferenceError: f2 is not defined at
複製代碼

3.4 eval()

飽受詬病的 eval()函數在嚴格模式下也獲得了提高。最大的變化就是它在包含上下文中再也不建立變量或函數。

3.4.1 使用 eval()建立變量

  • 非嚴格模式:彈出對話框顯示 10
  • 嚴格模式:調用 alert(x)時會拋出 ReferenceError
function doSomething()
  eval("var x=10"); 
  alert(x); 
}
doSomething();  //函數執行會彈出10
複製代碼
'use strict';
function doSomething()
  eval("var x=10"); 
  alert(x); 
}
doSomething(); //函數執行報錯:Uncaught ReferenceError: x is not defined at doSomething
複製代碼

若是是在非嚴格模式下,以上代碼會在函數 doSomething()中建立一個局部變量 x,而後 alert()還會顯示該變量的值。但在嚴格模式下,在 doSomething()函數中調用 eval()不會建立變量 x,所以調用 alert()會致使拋出 ReferenceError,由於 x 沒有定義。

3.4.2

能夠在 eval()中聲明變量和函數,但這些變量或函數只能在被求值的特殊做用域中有效,隨後就將被銷燬。所以,如下代碼能夠運行,沒有問題:

"use strict"
var result = eval("var x=10, y=11; x+y"); 
alert(result); //21 
複製代碼

這裏在 eval()中聲明瞭變量 xy,而後將它們加在一塊兒,返回了它們的和。因而,result 變量的值是 21,即 xy相加的結果。而在調用alert()時,儘管 xy 已經不存在了,result變量的值仍然是有效的。

3.5 eval 與 arguments

嚴格模式已經明確禁止使用evalarguments做爲標識符,也不容許讀寫它們的值。

3.5.1 把 eval 和 arguments 做爲變量引用

  • 非嚴格模式:沒問題,不出錯
  • 嚴格模式:拋出語法錯誤
var eval = 10
var arguments = "Hello world!";
console.log(eval,arguments); //10 "Hello world!"
複製代碼
'use strict'
var eval = 10
var arguments = "Hello world!";
console.log(eval,arguments); //Uncaught SyntaxError: Unexpected eval or arguments in strict mode
複製代碼

3.5.2 在非嚴格模式下,能夠重寫 eval,也能夠給 arguments 賦值。但在嚴格模式下,這樣作會致使語法錯誤。不能將它們用做標識符,意味着如下幾種使用方式都會拋出語法錯誤:

  • 使用 var 聲明;
  • 賦予另外一個值;
  • 嘗試修改包含的值,如使用++;
  • 用做函數名;
  • 用做命名的函數參數;
  • 在 try-catch 語句中用做例外名。

3.6 抑制this

JavaScript 中一個最大的安全問題,也是最容易讓人迷茫的地方,就是在某些狀況下如何抑制 this的值。

  • 非嚴格模式下使用函數的 apply()或 call()方法時,null 或 undefined 值會被轉換爲全局對象。
  • 嚴格模式下,函數的 this 值始終是指定的值,不管指定的是什麼值。

3.6.1 訪問屬性

  • 非嚴格模式:訪問全局屬性
  • 嚴格模式:拋出錯誤,由於 this 的值爲 null
var color = "red";
function displayColor({
   console.log(this.color);
}
displayColor.call(null); //'red'
複製代碼
'use strict';
var color = "red";
function displayColor({
   console.log(this.color);
}
displayColor.call(null); //Uncaught TypeError: Cannot read property 'color' of null at displayColor
複製代碼

以上代碼向displayColor.call()中傳入了null,若是在是非嚴格模式下,這意味着函數的this值是全局對象。結果就是輸出"red"。而在嚴格模式下,這個函數的 this的值是 null,所以在訪問 null的屬性時就會拋出錯誤。

3.7 其餘變化

3.7.1 with 的語句用法

首先是拋棄了 with 語句。

  • 非嚴格模式下: with語句可以改變解析標識符的路徑,容許
  • 嚴格模式下:with 被簡化掉了,拋出語法
with (location) {
  console.log(href);//http://127.0.0.1:5501/%E7%AC%AC%E5%85%AD%E5%91%A8/%E7%AC%AC%E4%BA%8C%E5%A4%A9/%E4%B8%A5%E6%A0%BC%E6%A8%A1%E5%BC%8F.html
}  
複製代碼
'use strict'
with (location) {
  console.log(href); //Uncaught SyntaxError: Strict mode code may not include a with statement

複製代碼

所以,在嚴格模式下使用 with會致使語法錯誤

3.7.2 使用八進制字面量

  • 非嚴格模式:值爲 8
  • 嚴格模式:拋出語法錯誤
var value = 010;
console.log(value);//8
複製代碼
'use strict'
var value = 010;
console.log(value);//Uncaught SyntaxError: Octal literals are not allowed in strict mode.
複製代碼

4. 參考書籍/網址

   本文參考了《javascript高級程序設計(第三版)》 以及 MDN網站。把內容進行了總結,每個的不一樣都使用代碼的形式展現了出來,但願對廣大朋友們有幫助。若是感受本文不錯,能夠點贊支持一下。能夠關注一下

相關文章
相關標籤/搜索