Strict Mode(嚴格模式)


Javascript中的物件導向

對於一個直譯式語言而言,物件導向特性是不是有那麼樣的重要,有很多不同的意見,有人認為物件導向除了會拖慢執行的速度,不良的物件導向語法,反而會出現更多不預期的程式臭蟲(bugs)。

Javascript中的物件導向存在有很多與現在流行語言完全不同的物件導向特性,也就是說如果你有學過Java、C++之類的物件導向語法,在Javascript中不是很難作到,就是要用很有技巧性的方式才能達成(trick),尤其是在舊的Javascript標準中,如果不依靠這些技巧,有些物件導向的特性根本很難作得到。當然,新的Javascript標準(ECMAScript5或更新的6)中,對於OOP已經有大幅度的改進。

網路上有關於Javascript的物件導向開發部份的教學,很多都是舊式的標準的內容,新的標準中已經有很多新的作法,相較於舊式的內容,使用上更為簡單而且安全。例如用於建立物件的Object.create方法,用於getter與getter的方法,以及許多方便開發者的Array方法。

本文專注於相容於目前大部份瀏覽器(與Node.js)的物件導向新特性,相容性可以到ECMAScript相容表觀看。

Strict Mode(嚴格模式)

Strict Mode是ECMAScript 5中的新功能之一,不過目前在瀏覽器端的開發並不建議使用(例如IE9就不支援),原因是大部份瀏覽器都只有部份支援而已,如果在不支援的瀏覽器上使用,也沒有效果(跳過)。在Node.js開發時則是建議使用,在每個程式檔案前頭加上:

"use strict";
var v = "after that, we are all strict mode script!";

也可以在某個函式中使用,不過只會在上下文中被限制為嚴格模式:

function imStrict(){
  "use strict";
  // ... after that, we are all strict mode script!
}

或是限定在某個函式庫(or外掛)中使用,不會影響到外面的程式碼:

// Non-strict code...

(function(){
  "use strict";

  // Define your library strictly...
})();

// Non-strict code...

經過Strict Mode宣告後,有部份的語法是會"嚴格"規定無法使用,這些語法通常是原本在Javascript中的小錯誤(Mistakes),現在則會變成中斷或無法執行的錯誤(Error)。

變數(Variable)與屬性(Property)

沒有經過宣告的變數與屬性會直接造成錯誤,如果沒有使用Strict前,則是會變成一個全域(global)物件的屬性:

"use strict"
a = 12;

變數要加上var宣告

delete運算子

delete運算子用在刪除變數或屬性、或陣列中的元素。用了Strict模式後,如果試圖要刪除不是可設定的(Configurable)物件屬性,也就是試圖刪除變數、函式、傳入值…等等,就會產生中斷執行的錯誤:

var foo = "test";
function test(){}

delete foo; // Error
delete test; // Error

function test2(arg) {
    delete arg; // Error
}

只有在刪除物件中的可設定的(Configurable)物件屬性才能使用:

window.b = 34;
delete window.b;

少用delete運算子,要使用時要注意能使用的場合只有一個情況

重覆性不允許

屬性名稱宣告


var myObject = {foo:1, bar:2, foo:1}; //throws an exception.

參數名稱

function print(value, value) {} //throws exception.

getter或setter

// SyntaxError
var foo = {
  get x() {},
  get x() {}
};

// the same with setters
// SyntaxError
var bar = {
  set y(value) {},
  set y(value) {}
};

不要使用重覆的變數或函式名稱

8進位(Octal)數用與跳脫(escape characters)宣告均不允許

這個宣告會不被允許主要原因是在Javascript中使用parseInt時會混亂。

var testStrict = 010; // throws exception
var testStrict = \010; // throws exception.

8進位可以用字串先宣告,處理上要注意。

“eval”與“arguments”不能作為變數、參數、函式、物件名稱

// All generate errors...
obj.eval = ...
obj.foo = eval;
var eval = ...;
for ( var eval in ... ) {}
function eval(){}
function test(eval){}
function(eval){}
new Function("eval")

用在物件內的屬性名稱倒是可以:

"use strict";

// OK
var foo = {
  eval: 10,
  arguments: 20
};

// OK
foo.eval = 10;
foo.arguments = 20;

eval也會變成沙盒環境(sandbox environment),意思是執行完就會把其中的變數或方法移除回收掉:

"use strict";

eval("var x = 10; alert(x);"); // 10
alert(x); // "x" is not defined

要破除這個規則可以使用indirect eval:

"use strict";

("indirect", eval)("var x = 10; alert(x);"); // 10
alert(x); // 10

eval的確少用為妙。不需要用保留字當變數或函式名稱。

未來保留字

這些未來Javascript可能使用到的保留字都不能使用在變數、方法名稱上:

  1. implements
  2. interface
  3. let
  4. package
  5. private
  6. protected
  7. public
  8. static
  9. yield

“this”關鍵字

this不是自動成為物件,而是預設變成undefined(或null)。這個undefined可以避免在使用constructors時忘了使用new關鍵字。

"use strict";

function A(x) {
  this.x = x;
}

// forget "new" keyword,
// error, because undefined.x = 10

var a = A(10);

var b = new A(10); // OK

this不要用在不是物件導向語法的全域方法中,會回傳undefined

with敘述不允許

with整個不能使用,這是有考量會混亂使用的情況。

Strict Mode優點

  • 協助程式碼中常見小問題(Mistakes)或不良語法(bad syntax)成為實際的中斷執行錯誤
  • 開發者可以更容易發覺到不安全的語法或一些物件導向設計程式碼的問題
  • 它關閉了某些容易造成混淆或誤用的語法或運算子

參考資料