严格模式
严格模式(strict mode):使 JavaScript 代码在更加严格的条件下运行。
严格模式不仅仅是一个子集,它故意(人为)设置与标准模式不同的语义。就是说,相同的代码在严格模式和标准模式下可能会有不同的行为表现。
如何开启严格模式?
1. 针对整个脚本文件的严格模式
在 script 标签下的第一行使用 "use strict";
代码开启严格模式。
若这行语句不是在第一行运行,那么脚本依然还是会使用“标准模式”运行。
示例:
<script>
// 针对脚本文件开启严格模式,整个文件会以严格模式运行
"use strict";
</script>
针对整个脚本文件的严格模式在文件合并时会出现冲突,比如严格模式和非严格模式的文件合并会出现冲突,导致运行错误。同样模式的文件合并不会出现这个问题,谨慎使用这个方式的严格模式。
2. 针对某个函数作用域的严格模式
在声明函数的时候,在函数体的第一行使用"use strict";
开启严格模式。
示例:
function fn(){
"use strict";
// 这个函数将会以严格模式调用
}
3. 折中的全局严格模式
由于第一种方法的严格模式存在文件合并冲突的问题,可以结合第二种方法进行折中的处理。
方法如下:
将整个文件的 JavaScript 代码包装到一个函数之中,在该函数中开启严格模式。使用这些 JavaScript 代码的方式就是调用这个函数,这样就可以间接地达到严格模式的要求,而又不会有文件合并冲突。
严格模式与标准模式的不同
1. 不允许隐式声明变量
在标准模式中,若在a = '字符串'
执行之前变量 a 没有被声明,那么JS引擎会自动在全局作用域之中隐式声明一个全局变量 a。
在严格模式中,类似a = '字符串'
的操作将会直接报错,声明变量必须显式使用关键字(var、let等)声明。
<script>
"use strict";
a = '字符串'; //Uncaught ReferenceError: a is not defined
</script>
这个特性可以避免某些情况下拼错变量名而造成的新增全局变量,影响后续代码运行的情况发生。
2. 严格模式会使标准模式下静默失败的代码报错
静默失败:代码执行失败,不报错。
常见的静默失败:
- 给对象不可写属性赋值
- 给对象只读属性赋值
- 给不可扩展对象的新属性赋值
- 删除对象中不可删除的属性
这些静默失败在标准模式下全部不会报错。而在严格模式下:
<script>
"use strict";
// 给不可写属性赋值
var obj1 = {}
Object.defineProperty(obj1,'x',{value:'我是obj1.x的值',writable:false}) // 给obj1设置一个x属性,值为'我是obj1.x的值',且设置为不可写属性
console.log(obj1.x) // 我是obj1.x的值
obj1.x = 9 // Uncaught TypeError: Cannot assign to read only property 'x' of object '#<Object>'
// 给只读属性赋值
var obj2 = { get x() {return 22;}} // 声明对象obj2,且给该对象设置一个值为22的只读属性 x
console.log(obj2.x) // 22
obj2.x = 9 // Uncaught TypeError: Cannot set property x of #<Object> which has only a getter
// 给不可扩展对象的新属性赋值
var obj3 = {} // 声明新对象 obj3
Object.preventExtensions(obj3) // 将 obj3 设置为不可扩展对象
obj3.newProp = "aaa" // Uncaught TypeError: Cannot add property newProp, object is not extensible
// 删除对象的不可删除属性
delete Object.prototype // Uncaught TypeError: Cannot delete property 'prototype' of function Object() { [native code] }
</script>
3. 重名的错误
1. 对象属性名重复
标准模式下,允许对象的属性名重复而不报错,但是对象之中只会有后面的那个属性(后面的属性覆盖前面的属性)。
<script>
var a = {
a : "aaa",
a : "bbb"
}
console.log(a) // {a: "bbb"}
</script>
而在严格模式下,对象属性名重复会报错(ES 6已经不报错,所以在ES 6的严格模式中,对象属性名重复和正常模式一样 )。
<script>
"use strict";
var a = {
a : "abb",
a : "bbb"
}; // 语法错误(我用的是支持ES 6的 Chrome 浏览器,所以不报错了。。。)
</script>
2. 函数形参名重复
标准模式下,函数形参名重复,最后的那一个形参会覆盖前面的同名形参,即只有后一个生效。参数依然可以使用伪数组 arguments[i] 来获取
function fn(a,a,b){
return a + b;
}
fn(1,2,3) // 5
严格模式下,函数形参名规定是唯一的,重负将会报错
function fn(a,a,b){
"use strict";
return a + b;
}
fn(1,2,3) // Uncaught SyntaxError: Duplicate parameter name not allowed in this context
4. 严格模式禁止八进制语法
标准模式下,数字前缀为 0 ,JS引擎会自动默认为八进制数。
var a = 010,
b = 011,
c = 020;
console.log(a) // 8
console.log(b) // 9
console.log(c) // 16
严格模式下,不支持八进制语法,将会报错。
"use strict";
var a = 010,
b = 011,
c = 020;
// 报错:Uncaught SyntaxError: Octal literals are not allowed in strict mode.
5. 严格模式禁止设置简单类型(number、string、symbol、boolean、null、undefined)的属性
标准模式下,给简单类型设置属性不会报错
(14).sailing = "home";
console.log((14).sailing = "home") // home
严格模式下,给简单类型设置属性会报错
"use strict";
(14).sailing = "home";
console.log((14).sailing = "home")
// 报错:Uncaught TypeError: Cannot create property 'sailing' on number '14'
6. 严格模式禁止删除变量
标准模式下,使用delete
关键字删除变量虽然不能删除成功,但是也不会报错
var x = 11;
delete x;
console.log(x) // 11
严格模式下,直接报错。
"use strict";
var x = 11;
delete x;
console.log(x)
// 报错:Uncaught SyntaxError: Delete of an unqualified identifier in strict mode.
不知道这种差别属不属于静默失败的范围,有知道的同学请给我留言,谢谢。
7. 静态绑定
严格模式只允许静态绑定属性和方法,即在编译时就确定方法和属性属于哪个对象。
1. 禁止使用 with
语句
with
语句有一个众所周知的缺陷——无法准确确定对象。所以,严格模式下,是不允许使用with
语句的。
2. 创设eval作用域
标准模式下,ES 6 之前的JavaScript 只有全局作用域和函数作用域两种作用域,而严格模式下,eval
函数也可以创设一个作用域,该作用域只在eval
函数中有效。
"use strict";
var x = 11;
eval("var x = 2;console.log(x)") // 2
console.log(x) // 11
8. 严格模式禁止 this 指向全局对象
严格模式下,this 禁止指向全局对象。
标准模式下,调用函数,函数中的 this
指向全局对象
function f(){
console.log(this)
}
f() // 打印出 Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, frames: Window, …}
严格模式下,this
指向undefined
function f(){
"use strict";
console.log(this);
}
f(); // undefined
9. 对 arguments 的限制
1. 不允许对 arguments 赋值
标准模式下,允许对arguments
进行赋值
function f(){
arguments = 1
console.log(arguments)
}
f(1,2,3); // 1
严格模式下,报错。
function f(){
"use strict";
arguments = 1
console.log(arguments)
}
f(1,2,3); // Uncaught SyntaxError: Unexpected eval or arguments in strict mode
2. 严格模式下,arguments
不再追踪参数的变化
标准模式
function f(a){
a = 1
console.log(a)
}
f(5); // 1
严格模式
function f(a) {
a = 2;
return [a, arguments[0]];
}
f(1); // 正常模式为[2,2]
function f(a) {
"use strict";
a = 2;
return [a, arguments[0]];
}
f(1); // 严格模式为[2,1]
3. 禁止使用arguments.callee
arguments.callee => 调用当前匿名函数的函数体(再执行一次)
也就是说,无法在匿名函数中调用自身了
"use strict";
var f = function(){ return arguments.callee}
f();
// 报错:Uncaught TypeError: 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them
总结
- 严格模式不允许隐式声明变量
- 严格模式会使标准模式下静默失败的代码报错
- 严格模式不允许函数形参名重复
- 严格模式禁止八进制语法
- 严格模式禁止设置简单类型(number、string、symbol、boolean、null、undefined)的属性
- 严格模式禁止删除变量
- 严格模式静态绑定属性和方法(禁止使用
with
、eval
作用域) - 严格模式下的
this
指向undefined
(未指定) - 严格模式对
arguments
对象的限制(不允许赋值、不追踪参数变化、禁止argument.callee,匿名函数无法调用自身)