JavaScript嚴格模式
介紹
ECMAscript 5添加了“嚴格模式”,會使得Javascript在更嚴格的條件下運行,設立"嚴格模式"的目的,主要有以下幾個:
- 消除Javascript語法的一些不合理、不嚴謹之處,減少一些怪異行為;
- 消除代碼運行的一些不安全之處,保證代碼運行的安全;
- 提高編譯器效率,增加運行速度;
- 為未來新版本的Javascript做好鋪墊。
說明
此文參考了阮一峰的Javascript 嚴格模式詳解,做個總結筆記。對於原文中的例子我都一一測試了,但有個別測試結果是不一樣的,後面已做說明。那些我也是比較迷惑的,如果有錯誤,希望指出。
開始
啟用嚴格模式很簡單,只需要一個字符串
"use strict";
但是這個字符串的位置不是隨意放的,有兩種:
- 全局嚴格模式,放在腳本文件的第一行
- 局部嚴格模式,放在函數內第一行(推薦)
為什麽推薦使用在函數內的嚴格模式呢?
因為全局嚴格模式不利於代碼的合並,團隊多人開發時,合並代碼可能會使別人某些代碼失效。
所以更好的做法是,借用局部嚴格模式方法,將整個腳本文件放在一個立即執行的匿名函數之中:
(function (){ "use strict"; // some code here })();
思考
為什麽是一個字符串啟用嚴格模式?是為了兼容老舊的瀏覽器,一行字符串不會對不兼容嚴格模式的瀏覽器產生影響。
改變
嚴格模式帶來了很多語法的改變。
變量賦值前必須聲明
通常我們可以直接對一個變量賦值而不需要提前var
聲明,此時這個變量就是全局變量。嚴格模式禁止這種用法,全局變量必須顯式聲明。
"use strict" a = 2; //報錯
因此,嚴格模式下,變量都必須先用var
命令聲明,然後再使用。
禁止使用with
正常模式下,我們可以使用with
來改變作用域鏈,如:
var obj = { num:1 } function test(){ var num = 2; with(obj){ console.log(num); } } test(); //1
但是在嚴格模式下,禁用了with
,報錯:
"use strict"; test(); //Strict mode code may not include a with statement
創建eval作用域
正常模式下,Javascript語言有兩種變量作用域(scope):全局作用域和函數作用域。嚴格模式創設了第三種作用域:eval作用域。
正常模式下,eval
語句的作用域,取決於它處於全局作用域,還是處於函數作用域。嚴格模式下,eval
語句本身就是一個作用域,不再能夠生成全局變量了,它所生成的變量只能用於eval
內部。
"use strict"; var x = 5; console.log(eval(‘var x = 10;console.log(x)‘)); //10 console.log(x); //5
局部this必須賦值
正常模式下,函數編譯時,內部this
指向的是全局window對象,但是嚴格模式時,this
不再指向window
,而是undefined
。你需要自己手動賦值,賦值是什麽,this
就是什麽。
"use strict"; console.log(‘window: ‘,this); //window function test(){ console.log(‘test: ‘,this); //undefined } test();
因此,使用構造函數時,如果忘了加new,this不再指向全局對象,而是報錯。
function fn(){ "use strict"; this.a = 1; }; fn(); // 報錯
arguments對象的限制
arguments是函數的參數對象,嚴格模式對它的使用做了限制。
不允許對arguments賦值
"use strict"; var arguments = 5; //報錯 function arguments(){ //報錯 //some code }
arguments不再追蹤參數的變化
//正常模式 function test(a){ a = 5; console.log([a,arguments[0]]) } test(2); //[5,5] //嚴格模式 "use strict"; function test(a){ a = 5; console.log([a,arguments[0]]) } test(2); //[5,2]
禁止使用arguments.callee
arguments.callee可以返回正被執行的函數對象
//正常模式 function test(){ console.log(arguments.callee); } test(); // test(){ // console.log(arguments.callee); //}
嚴格模式不允許再使用arguments.callee
禁止使用caller
正常模式下,可以使用caller返回一個函數對象,這個函數調用了當前函數:
function test(){ demo(); } function demo(){ console.log(demo.caller); } test(); // test(){ // demo(); //}
嚴格模式禁止再使用caller。所以兩個長得很像的callee和caller在嚴格模式下都不能再使用。
函數必須聲明在頂層
什麽意思呢?
我們都只到es6引入了塊級作用域,為了與新版本接軌,嚴格模式只允許在全局作用域或函數作用域的頂層聲明函數。也就是說,不允許在非函數的代碼塊內聲明函數。
//正常模式 if(true){ function fn(){ console.log(‘fn‘); } fn(); // fn } fn(); // fn
"use strict"; if(true){ function fn(){ console.log(‘fn‘); } fn(); // fn } fn(); // 報錯 fn is not defined
重名錯誤
對象不能有重名屬性
按照 阮一峰的文章,在嚴格模式下,對象是不準有重名屬性的,會報錯。但實際測試中(谷歌瀏覽器),嚴格模式下,對象屬性重名並不會報錯,而是像正常模式一樣,後面的覆蓋前面的。
"use strict"; var obj = { a:1, a:2 } console.log(obj.a); //2
後來了解到,ES6中的嚴格模式已經允許對象有重名的屬性。如果有了解的,可以告訴我下。
函數不能有重名的參數
正常模式下,參數重名,後面的覆蓋前面的:
function test(a,a,b){ console.log(a,a,b); } test(1,2,3); // 2 2 3
但在嚴格模式下,會報錯:
"use strict"; function test(a,a,b){ console.log(a,a,b); } test(1,2,3); // 報錯 Duplicate parameter name not allowed in this context
禁止刪除變量
按照 阮一峰的文章,嚴格模式下無法刪除變量。只有configurable設置為true的對象屬性,才能被刪除。
但是即使在正常模式下,用var
聲明的變量也是無法刪除的,不管是全局聲明還是局部聲明,不過可以刪除對象屬性:
var obj = { a:2 } delete obj.a; //true obj //{}
在嚴格模式下,用var
聲明的變量也是無法刪除的,但對象的屬性也是可以刪除的:
"use strict"; var obj = { a:2 } delete obj.a; //true obj //{}
需要註意的是,在正常模式下,即使變量不可以刪除,你也可以寫入 delete
,不會報錯,但是嚴格模式下,刪除不了的變量不可以用delete
:
"use strict"; var a = 2; var obj = { b:3 } delete a; //報錯,不可刪除就不能使用delete
禁止八進制表示法
正常模式下,整數的第一位如果是0,表示這是八進制數,比如0100等於十進制的64。嚴格模式禁止這種表示法,整數第一位為0,將報錯。
"use strict"; var num = 0100; console.log(num); // 報錯
但是es6提供了一種八進制數的新表示法,就是在數值前加上0o(第一個是數字0,第二個是字母o)
"use strict"; var num = 0o100; console.log(num);
保留字
為了向將來Javascript的新版本過渡,嚴格模式新增了一些保留字:implements, interface, let, package, private, protected, public, static, yield。
使用這些詞作為變量名將會報錯。
"use strict"; var let; //報錯
說到保留字let
,我們知道es6已經加入了let
和const
,const
不管是在正常模式還是嚴格模式下都不可作為變量名。因為各大瀏覽器自行增加的const
保留字,所以不能作為變量名的。
JavaScript嚴格模式