ECMAScript5將嚴格模式(strict mode)引入了Javascript中,目的是容許開發人員可以選擇「更好」的Javascript版本,這個版本能用不一樣的方式處理那些廣泛而又臭名昭著的錯誤。一開始的時候,我對該模式抱着懷疑的態度,由於當時在只有一款瀏覽器(Firefox)支持嚴格模式。時至今日,全部的主流瀏覽器的最新版本——包括IE10與Opera12——都支持嚴格模式。使用嚴格模式的時機已經成熟了。
嚴格模式給Javascript的運行方式帶來了許多不一樣,我將它們分爲了兩類:明顯的(obvious),以及微妙的(subtle)。那些微妙的改變是爲了解決微妙的問題,我不打算在這裏對其進行贅述。若是你對這些細節感興趣,請參考Dmitry Soshnikov的精彩文章,《ECMA-262-5 in Detail. Chapter 2. Strict Mode》。我對介紹明顯的變化更感興趣:它們是你開始使用嚴格模式以前所必須瞭解的,也可能給你帶來最多好處。 express
在開始介紹特殊特性以前,你須要記住,嚴格模式的目標之一是容許更快地調試錯誤。幫助開發者調試的最佳途徑是當肯定的問題發生時拋出相應的錯誤(throw errors when certain patterns occur),而不是悄無聲息地失敗或者表現出奇怪的行爲(這正是現在不在嚴格模式下的Javascript作的)。嚴格模式下的代碼拋出更多的錯誤信息,這是好事,由於它能幫助開發者很快注意到一些必須當即解決的問題。 瀏覽器
首先,嚴格模式去除了with語句。當with語句出如今嚴格模式中時,它會被認爲是非法的Javascript語句並拋出語法錯誤。因此,使用嚴格模式的第一步就是確保你沒有在使用with。 安全
// 在嚴格模式中將致使語法錯誤 with (location) { alert(href); }
第二點是,變量在賦值以前必須聲明。在非嚴格模式下,給一個未聲明的變量賦值將自動生成一個該名字的全局變量。這是Javascript中最廣泛的錯誤之一。嚴格模式中,這樣作將拋出一個錯誤。 ecmascript
// 嚴格模式中拋出一個錯誤 (function() { someUndeclaredVar = "foo"; }());
另外一個重要的變化是,當this值爲null或undefined時,不會再將其強制轉換爲全局對象。也就是說,this保留了它的原始值,也所以可能會致使一些依賴於強制轉換的代碼發生錯誤。例如: ide
window.color = "red"; function sayColor() { // 嚴格模式下,this不會指向window alert(this.color); } // 如下兩種狀況,在嚴格模式下都拋出錯誤 sayColor(); sayColor.call(null);
根本而言,this值必須賦值,不然將保留undefined值。這意味着調用構造函數時若漏掉了new關鍵字也會致使錯誤: 函數
function Person(name) { this.name = name; } // 嚴格模式下致使錯誤 var me = Person("Nicholas");
在這段代碼裏,調用Person構造函數時缺乏了new關鍵字,此時this值爲undefined。因爲你不能給undefined添加屬性,這段代碼拋出了一個錯誤。在非嚴格模式下,this會強制轉換爲全局對象,所以name屬性可以被正確賦值爲全局變量。 性能
當你作了大量的編碼的時候,你很容易在對象中定義了重複的屬性或者給函數定義了重複的參數名。嚴格模式下,這兩種狀況都會致使錯誤的發生: 測試
// 嚴格模式下錯誤 - 重複參數 function doSomething(value1, value2, value1) { //code } // 嚴格模式下錯誤 - 重複屬性 var object = { foo: "bar", foo: "baz" };
這二者都是語法錯誤,在代碼執行以前將拋出錯誤。 this
eval()沒有被移除,但它在嚴格模式下發生了一些變化。最大的改變是:在eval()語句中聲明的變量以及函數不會在包含域中建立。例如: 編碼
(function() { eval("var x = 10;"); // 非嚴格模式下,x爲10 // 嚴格模式下,x沒有聲明,拋出一個錯誤 alert(x); }());
任意由eval()建立的變量或函數仍呆在eval()裏。然而,你能夠經過從eval()中返回一個值的方式實現值的傳遞:
(function() { var result = eval("var x = 10, y = 20; x + y"); // 嚴格模式與非嚴格模式下都能正常工做(獲得30) alert(result); }());
ECMAScript 5 同時引入了修改屬性特徵的能力,例如設置一個屬性爲只讀或者凍結整個對象的結構(freezing an entire object’s structure)。在非嚴格模式下,試圖修改一個不可變的屬性時將悄無聲息地失敗。你可能在使用一些原生APIs的時候已經遇到這類問題。嚴格模式將保證不管你在什麼時候試圖使用一種不被容許的方式修改一個對象或對象的屬性時拋出錯誤。
var person = {}; Object.defineProperty(person, "name" { writable: false, value: "Nicholas" }); // 非嚴格模式下將悄無聲息地失敗,嚴格模式則拋出錯誤 person.name = "John";
這個例子中,name屬性被設置爲只讀。在非嚴格模式下,對name的賦值將悄無聲息地失敗;而在嚴格模式下,一個錯誤將被拋出。
注:若是你在使用ECMAScript屬性能力(the ECMAScript attribute capabilities),我強烈推薦你開啓嚴格模式。若是你在改變對象的可變性(mutability of objects),你將遇到一堆錯誤,而它們在非嚴格模式下將被安靜地帶過。
在現代瀏覽器中很容易啓用嚴格模式,只需添加下面一條語句:
"use strict";
雖然這看起來只是一個沒有賦值給變量的字符串,但它確實地指示了Javascript引擎切換爲嚴格模式(那些不支持嚴格模式的瀏覽器只是簡單地讀取了這個字符串而後繼續像日常同樣運行)。你能夠在全局或函數中使用它。話雖這麼說,你永遠不該該在全局中使用它。全局地使用這條指示,意味着同個文件下的全部代碼都在嚴格模式下運行。
// 別這麼作 "use strict"; function doSomething() { // 這將在嚴格模式下運行 } function doSomethingElse() { // 這也是 }
這看起來彷佛不是個大問題,但在咱們這個不一樣腳本堆積在一塊兒的世界裏(our world of aggressive script concatenation)將致使大麻煩。只要有一份腳本全局地包含這條指令,其它串聯的腳本也將在嚴格模式下運行(可能引起一些你從沒預想到的錯誤)。
所以,最好只在函數內使用嚴格模式,例如:
function doSomething() { "use strict"; // 嚴格模式下運行 } function doSomethingElse() { // 非嚴格模式下運行 }
若是你想將嚴格模式應用於多個函數,可使用以下模式( immediately-invoked function expression (IIFE)):
(function() { "use strict"; function doSomething() { // this runs in strict mode } function doSomethingElse() { // so does this } }());
我強烈建議每個人都開始使用嚴格模式。如今已經有足夠多的瀏覽器支持該模式,它將把你從藏身代碼的錯誤中拯救出來。你須要確保你沒有全局地包含啓用指令,但能夠頻繁地使用IIFEs給任意多的代碼應用嚴格模式。一開始,你將碰到從沒遇過的錯誤——這是很正常的。切換到嚴格模式後,你須要作足夠多的測試來保證你已hold住你的代碼。必定不能只是將「use strict」扔進你的代碼而後就假設不會有錯誤發生。至少的至少,你該開始使用這個異常有用的語言特性來寫更好的代碼了。