严格模式

严格模式的理解

概念

理解:除了正常运行模式(混杂模式),ES5添加了第二种运行模式:”严格模式”(strict mode)。

顾名思义,这种模式使得Javascript在更严格的语法条件下运行。

目的

  • 消除Javascript语法的一些不合理、不严谨之处,减少一些怪异行为

  • 消除代码运行的一些不安全之处,为代码的安全运行保驾护航

  • 为未来新版本的Javascript做好铺垫

使用

  • 针对整个脚本文件:将use strict放在脚本文件的第一行,则整个脚本文件将以严格模式运行。

  • 针对单个函数:将use strict放在函数体的第一行,则整个函数以严格模式运行。

PS:如果浏览器不支持,则这句话只解析为一条简单的语句, 没有任何副作用。

脚本文件的变通写法:因为第一种调用方法不利于文件合并,所以更好的做法是,借用第二种方法,将整个脚本文件放在一个立即执行的匿名函数之中。

严格模式和普通模式的区别

1.全局变量必须显式声明

在正常模式中,如果一个变量没有声明就赋值,默认是全局变量。

严格模式禁止这种用法,全局变量必须显式声明

示例:

1
2
3
4
5
"use strict";
name="xiaomi";//报错,name未声明
for(item in array){
//报错,item未声明
}

在严格模式下,变量都必须先用varletconst声明,然后再使用。

2.禁止动态绑定

好处:在编译时就确定属性与方法到底归属哪个对象,有利于编译效率的提高,也有助于代码的阅读。

动态绑定:即某些属性和方法到底属于哪个对象,不是在编译时确定,而是在运行时确定。

哪些使用了动态绑定?

1.with语句

1
2
3
4
5
6
7
8
9
10
11
"use strict";

var obj={
name:'xiaomi',
count:12
};
//使用with语句报错
with(obj){
name:'xiaowang',
sex:"男"
}

为什么使用with语句,不确定属性的归属呢?

很简单,在正常模式下:with绑定的是obj对象,name属性在obj中,而sex不在obj中,则最终会将sex声明为为全局变量。

2.eval作用域

正常模式下:eval语句的作用域取决于它处于全局作用域,还是函数作用域。

严格模式下:eval语句本身就是一个作用域,它生成的变量只能在eval内部使用。

1
2
3
4
"use strict";
var name='xiaoming';
console.log(eval("var name='xiaohua';name"))//'xiaohua'
console.log(name);//'xiaoming'

3.禁止this指向全局对象,而是指向undefined

1
2
3
4
5
6
7
8
9
var name="xiaoming";
function foo(){
console.log(this.name);//'xiaoming',this指向window
}

function foo(){
"use strict";
console.log(this.name);//抛出错误,因为this为undefined
}

4.禁止删除变量

严格模式下,声明的变量无法被删除

对象中的属性,若设置了configurable:true,那么这个对象的属性是可以被删除的。

1
2
3
4
5
6
7
8
"use strict";
var name;
delete name; // 语法错误
var obj = Object.create(null, {'count': {
value: 1,
configurable: true
}});
delete obj.count; // 删除成功

5.禁止了不在脚本或者函数层面上的函数声明

所谓的顶层是在全局中声明,即在块作用域中声明是错误的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
"use strict";
if (true) {
function f() { } // !!! 语法错误
f();
}

for (var i = 0; i < 5; i++) {
function f2() { } // !!! 语法错误
f2();
}

function baz() { // 合法
function eit() { } // 同样合法
}

6.禁止变量或函数参数重名

重名:在同一作用域,两个或两个以上变量名相同。

正常模式下,如果对象有多个重名属性,那么最后一个会覆盖前面的值。

但在严格模式下这是会报错的。

1
2
3
4
5
"use strict";
var obj={
name:"xiaoming",
name:"xiaohua"
};//报错

正常模式下:在函数参数中,如果参数有重名情况,我们可以使用arguments[i]读取参数,以便区分参数

1
2
3
4
5
6
function foo(a,b,b,c){
console.log(arguments[0]);//a
console.log(arguments[1]);//第一个b
console.log(arguments[2]);//第二个b
console.log(arguments[3]);//c
}

上面重复参数名,在严格模式下报错。

7.限制了arguments对象

1.不允许对arguments赋值

正常模式下:

1
2
3
4
5
6
function foo(a,b){
console.log("赋值前:",arguments)
arguments=12;
console.log("赋值后:",arguments)
}
foo(1,2);

严格模式下会报错。

2.arguments不再跟踪参数的变化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function foo(a,b){
a=11;
b=22;
return arguments;
}
console.log("正常模式下:",foo(1,2));//Arguments(2) [11, 22, callee: ƒ, Symbol(Symbol.iterator): ƒ]

function bar(a,b){
"use strict";
a=33;
b=44;
return arguments;
}
console.log("严格模式下:",bar(3,4));//Arguments(2) [3, 4, callee: (...), Symbol(Symbol.iterator): ƒ]

严格模式下,a,b已经重新赋值,但是arguments没有记录它们的变化。