介绍
ECMAscript 5添加了“严格模式”,会使得Javascript在更严格的条件下运行,设立”严格模式”的目的,主要有以下几个:
- 消除Javascript语法的一些不合理、不严谨之处,减少一些怪异行为;
- 消除代码运行的一些不安全之处,保证代码运行的安全;
- 提高编译器效率,增加运行速度;
- 为未来新版本的Javascript做好铺垫。
说明
此文参考了阮一峰的Javascript 严格模式详解,对于原文中的例子我都一一测试了,有个别测试结果是不一样的,后面已做说明。那些我也是比较迷惑的,如果有错误,希望指出。
开始
启用严格模式很简单,只需要一个字符串1
"use strict";
但是这个字符串的位置不是随意放的,有两种:
- 全局严格模式,放在脚本文件的第一行
- 局部严格模式,放在函数内第一行(推荐)
为什么推荐使用在函数内的严格模式呢?
因为全局严格模式不利于代码的合并,团队多人开发时,合并代码可能会使别人某些代码失效。
所以更好的做法是,借用局部严格模式方法,将整个脚本文件放在一个立即执行的匿名函数之中:1
2
3
4
5
6(function (){
"use strict";
// some code here
})();
思考
为什么是一个字符串启用严格模式?是为了兼容老旧的浏览器,一行字符串不会对不兼容严格模式的浏览器产生影响。
改变
严格模式带来了很多语法的改变。
变量赋值前必须声明
通常我们可以直接对一个变量赋值而不需要提前var
声明,此时这个变量就是全局变量。严格模式禁止这种用法,全局变量必须显式声明。1
2
3"use strict"
a = 2; //报错
因此,严格模式下,变量都必须先用var
命令声明,然后再使用。
禁止使用with
正常模式下,我们可以使用with
来改变作用域链,如:1
2
3
4
5
6
7
8
9
10var obj = {
num:1
}
function test(){
var num = 2;
with(obj){
console.log(num);
}
}
test(); //1
但是在严格模式下,禁用了with
,报错:1
2
3
4"use strict";
test();
//Strict mode code may not include a with statement
创建eval作用域
正常模式下,Javascript语言有两种变量作用域(scope):全局作用域和函数作用域。严格模式创设了第三种作用域:eval作用域。
正常模式下,eval
语句的作用域,取决于它处于全局作用域,还是处于函数作用域。严格模式下,eval
语句本身就是一个作用域,不再能够生成全局变量了,它所生成的变量只能用于eval
内部。1
2
3
4
5
6
7"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
就是什么。1
2
3
4
5
6
7
8
9"use strict";
console.log('window: ',this); //window
function test(){
console.log('test: ',this); //undefined
}
test();
因此,使用构造函数时,如果忘了加new,this不再指向全局对象,而是报错。1
2
3
4
5function fn(){
"use strict";
this.a = 1;
};
fn(); // 报错
arguments对象的限制
arguments是函数的参数对象,严格模式对它的使用做了限制。
不允许对arguments赋值
1 | "use strict"; |
arguments不再追踪参数的变化
1 | //正常模式 |
禁止使用arguments.callee
arguments.callee可以返回正被执行的函数对象1
2
3
4
5
6
7
8
9//正常模式
function test(){
console.log(arguments.callee);
}
test();
// test(){
// console.log(arguments.callee);
//}
严格模式不允许再使用arguments.callee
禁止使用caller
正常模式下,可以使用caller返回一个函数对象,这个函数调用了当前函数:1
2
3
4
5
6
7
8
9
10
11
12function test(){
demo();
}
function demo(){
console.log(demo.caller);
}
test();
// test(){
// demo();
//}
严格模式禁止再使用caller。所以两个长得很像的callee和caller在严格模式下都不能再使用。
函数必须声明在顶层
什么意思呢?
我们都只到es6引入了块级作用域,为了与新版本接轨,严格模式只允许在全局作用域或函数作用域的顶层声明函数。也就是说,不允许在非函数的代码块内声明函数。
1 | //正常模式 |
1 | "use strict"; |
重名错误
对象不能有重名属性
按照 阮一峰的文章,在严格模式下,对象是不准有重名属性的,会报错。但实际测试中(谷歌浏览器),严格模式下,对象属性重名并不会报错,而是像正常模式一样,后面的覆盖前面的。1
2
3
4
5
6
7
8"use strict";
var obj = {
a:1,
a:2
}
console.log(obj.a); //2
后来了解到,ES6中的严格模式已经允许对象有重名的属性。如果有了解的,可以告诉我下。
函数不能有重名的参数
正常模式下,参数重名,后面的覆盖前面的:1
2
3
4
5function test(a,a,b){
console.log(a,a,b);
}
test(1,2,3); // 2 2 3
但在严格模式下,会报错:1
2
3
4
5
6
7
8"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
声明的变量也是无法删除的,不管是全局声明还是局部声明,不过可以删除对象属性:1
2
3
4
5
6
7var obj = {
a:2
}
delete obj.a; //true
obj //{}
在严格模式下,用var
声明的变量也是无法删除的,但对象的属性也是可以删除的1
2
3
4
5
6
7
8
9"use strict";
var obj = {
a:2
}
delete obj.a; //true
obj //{}
需要注意的是,在正常模式下,即使变量不可以删除,你也可以写入 delete
,不会报错,但是严格模式下,删除不了的变量不可以用delete
:1
2
3
4
5
6
7
8
9"use strict";
var a = 2;
var obj = {
b:3
}
delete a; //报错,不可删除就不能使用delete
禁止八进制表示法
正常模式下,整数的第一位如果是0,表示这是八进制数,比如0100等于十进制的64。严格模式禁止这种表示法,整数第一位为0,将报错。1
2
3"use strict";
var num = 0100;
console.log(num); // 报错
但是es6提供了一种八进制数的新表示法,就是在数值前加上0o(第一个是数字0,第二个是字母o)
1 | "use strict"; |
保留字
为了向将来Javascript的新版本过渡,严格模式新增了一些保留字:implements, interface, let, package, private, protected, public, static, yield。
使用这些词作为变量名将会报错。1
2
3"use strict";
var let; //报错
说到保留字let
,我们知道es6已经加入了let
和const
,const
不管是在正常模式还是严格模式下都不可作为变量名。因为各大浏览器自行增加的const
保留字,所以不能作为变量名的。