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
保留字,所以不能作为变量名的。