1. 程式人生 > >是否該使用嚴格模式?use strict

是否該使用嚴格模式?use strict

其實這篇文章是在2012年就寫的,想想自己落後了幾年了。很早一段時間我就看到過介紹嚴格模式的文章,但當時還是沒有重視,因為兩種模式下的區別還是挺大的,長年的書寫習慣讓我不太想去改變。但後來在我優化自己的程式碼的過程中發現,目前的程式碼確實是有點‘亂’,除了原本程式碼不夠優美之外,還缺乏明確性,如果非要拿出一個來說,就是this的指向問題,當this為null或者undefined的時候,被強制轉換成window了,這樣當他出現錯誤的時候,可能會不報錯。或者把我們引導到錯誤的地方。

而且在非嚴格模式下,某些程式碼錯誤的提示會指向莫名其妙的地方,而嚴格模式則會明確指出是哪裡出了問題。看完以下文章,我想你肯定會急切更改自己原來的程式碼。如果你還沒開始使用use strict的話.

目前支援嚴格模式的瀏覽器包括IE10+ ,Firefox4+ ,safari5.1+ ,Opera 12+,Chrome

ECMAScript5將嚴格模式(strict mode)引入了Javascript中,目的是允許開發人員能夠選擇“更好”的Javascript版本,這個版本能用不同的方式處理那些普遍而又臭名昭著的錯誤。一開始的時候,我對該模式抱著懷疑的態度,因為當時在只有一款瀏覽器(Firefox)支援嚴格模式。時至今日,所有的主流瀏覽器的最新版本——包括IE10與Opera12——都支援嚴格模式。使用嚴格模式的時機已經成熟了。

它帶來了什麼?

嚴格模式給Javascript的執行方式帶來了許多不同,我將它們分為了兩類:明顯的(obvious)

,以及微妙的(subtle)。那些微妙的改變是為了解決微妙的問題,我不打算在這裡對其進行贅述。如果你對這些細節感興趣,請參考Dmitry Soshnikov的精彩文章,《ECMA-262-5 in Detail. Chapter 2. Strict Mode》。我對介紹明顯的變化更感興趣:它們是你開始使用嚴格模式之前所必須瞭解的,也可能給你帶來最多好處。

在開始介紹特殊特性之前,你需要記住,嚴格模式的目標之一是允許更快地除錯錯誤。幫助開發者除錯的最佳途徑是當確定的問題發生時丟擲相應的錯誤(throw errors when certain patterns occur),而不是悄無聲息地失敗或者表現出奇怪的行為(這正是如今不在嚴格模式下的Javascript做的)。嚴格模式下的程式碼丟擲更多的錯誤資訊,這是好事,因為它能幫助開發者很快注意到一些必須立即解決的問題。

去除with語句(Eliminates with)

首先,嚴格模式去除了with語句。當with語句出現在嚴格模式中時,它會被認為是非法的Javascript語句並丟擲語法錯誤。所以,使用嚴格模式的第一步就是確保你沒有在使用with。

// 在嚴格模式中將導致語法錯誤
with (location) {
alert(href);
}
防止意外的全域性變數(Prevents accidental globals),比如說假設href是全域性變數的話

第二點是,變數在賦值之前必須宣告。在非嚴格模式下,給一個未宣告的變數賦值將自動生成一個該名字的全域性變數。這是Javascript中最普遍的錯誤之一。嚴格模式中,這樣做將丟擲一個錯誤。

// 嚴格模式中丟擲一個錯誤
(function() {
someUndeclaredVar = “foo”;
}());
取消this值的強制轉換(Eliminates this coercion)

另一個重要的變化是,當this值為null或undefined時,不會再將其強制轉換為全域性物件。也就是說,this保留了它的原始值,也因此可能會導致一些依賴於強制轉換的程式碼發生錯誤。例如:

window.color = “red”;
function sayColor() {
// 嚴格模式下,this不會指向window
alert(this.color);
}

// 以下兩種情況,在嚴格模式下都丟擲錯誤
sayColor();
sayColor.call(null);
根本而言,this值必須賦值,否則將保留undefined值。這意味著呼叫建構函式時若漏掉了new關鍵字也會導致錯誤:

//"use strict"
function Person(name) {
    this.name = name;  
    console.log(this+this.name);
}  

//Person('a');//window   =>a
//new Person('w');//object =>w
//嚴格模式:Person('a')報錯,cannot set property name of undefined

在這段程式碼裡,呼叫Person建構函式時缺少了new關鍵字,此時this值為undefined。由於你不能給undefined新增屬性,這段程式碼丟擲了一個錯誤。在非嚴格模式下,this會強制轉換為全域性物件,因此name屬效能夠被正確賦值為全域性變數。

拒絕重複(No duplicates)

當你做了大量的編碼的時候,你很容易在物件中定義了重複的屬性或者給函式定義了重複的引數名。嚴格模式下,這兩種情況都會導致錯誤的發生:

// 嚴格模式下錯誤 - 重複引數
function doSomething(value1, value2, value1) {
//code
}

// 嚴格模式下錯誤 - 重複屬性
var object = {
foo: “bar”,
foo: “baz”
};
這兩者都是語法錯誤,在程式碼執行之前將丟擲錯誤。

更安全的eval()(Safer eval())

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);  

}());
在嚴格模式下,eval裡面的變數只在eval裡面有效,這就相當於是內部函式的變量了。在外部定義的變數,在eval中可以不用var 宣告。比如var x=eval("x=1;++x");是不會報錯的
不可改變引發的錯誤(Errors for immutables)

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”扔進你的程式碼然後就假設不會有錯誤發生。至少的至少,你該開始使用這個異常有用的語言特性來寫更好的程式碼了。