最近在看周爱民老师出版的JavaScript语言精髓与编程实践第三版,让我重新认识了js的严格模式,今天就分享一下我的学习笔记
### 简介
JavaScript从ES5开始支持严格模式,需要使用字符串序列来开启:“use strict”;
在代码中他是一个字符串字面量,被用在一段代码文本的最前面,作为“指示前缀”
由于JavaScript中的代码块是按语句来解析的,因此在"use strict"后面加上分号或回车符就可以将该指示前缀解释成“字面量表达式语句”,从而开启相应代码块中的严格模式
### 开启严格模式的两种方式
#### 1、显式声明开启
###### 1、在全局代码的开始处加入
###### 2、在eval代码开始处加入
###### 3、在函数声明代码开始处加入
###### 4、在new Function()所传入的body参数块开始处加入
####2、默认开启
###### 1、模块中
###### 2、类声明和类表达式的整个声明块中
###### 3、在引擎或宿主的运行参数中指定,如:“node --use_strict”
### 严格模式下的限制
#### 语法限制
语法限制是指如果代码文本中出现了违例,则在语法分析期该代码文本就是无效的,其所在的代码块完全不能装载执行(提示语法错误异常)
##### 严格模式下导致语法错误异常的情况
###### 1、在对象字面量声明中存在相同的属性名
```javascript
// 严格模式下会报语法错误
var obj = {
name: 'kim',
name: 'xie',
}
```
###### 2、在函数声明中,参数表中带有相同的参数名
```javascript
// 严格模式下会报语法错误
function fn(x,x,y,z,y){
return x + y;
}
// 非严格模式下,访问同名的参数时,只有最后一个声明时有效的
fn(1,2,3,4,5) => return 7
fn(1,2) => return NaN
```
###### 3、不能声明或重写eval和arguments这两个标识符
```javascript
// 严格模式下会报语法错误
// 1、向eval和arguments赋值
eval = function(){}
// 2、向eval和arguments重新声明
var arguments;
// 3、将eval和arguments用作catch子句的异常对象名
try{
// ...
}catch(eval){
// ...
}
// 4、将eval或arguments用作函数名
function arguments(){
// ...
}
// 5、删除arguments或形参名
function fn(){
delete arguments;
}
```
###### 4、用0前缀声明的八进制字面量
```javascript
// 严格模式下会报语法错误
var num = 012;
console.log(num);
// 非严格模式下打印 10
```
###### 5、用delete删除显式声明的标识符、名称或具名函数
```javascript
// 严格模式下会报语法错误
// 删除变量
var x;
delete x;
// 删除具名函数
function fn(){};
delete fn;
// 删除函数形参
function fn(x){
delete x;
}
// 删除catch子句声明的异常对象
try {
// ...
}catch (e){
delete e;
}
// 非严格模式下,这些操作默认是“无效的”,不会抛出异常
// 如果delete删除其他一些不能被删除的对象属性、标识符时将导致执行期异常
```
###### 6、在代码中使用一些扩展的保留字,保留字包括:implements、interface、let、package、private、protected、public、static以及yield
```javascript
// 严格模式下会报语法错误
var yield;
function let(){}
```
###### 7、在代码中包括with语句
```javascript
// 严格模式下会报语法错误
function(){
with(arguments) return length;
}
// 严格模式下with语句直接被禁止了
```
#### 执行限制
执行限制是指将会导致运行期错误的限制
##### 严格模式下导致运行期异常的情况
###### 1、向不存在的标识符赋值将导致“引用异常”
```javascript
// 严格模式下会报执行异常
// 向不存在的标识符赋值
a = '123';
// 由于js中允许局部变量访问上级作用域链的变量
// 所以在语法分析阶段并不能确认a是否真实存在执行环境中
// 正确的做法是,显式声明,或确保外层作用域存在这个变量名
"use strict";
var a = '123';
function fn(){
a="1";
}
```
###### 2、运算符处理一些不可处理的操作数时将导致“类型异常或语法错误”
```javascript
// 严格模式下会报执行异常
var obj = {
x: 100
}
// 1、当对象是不可扩展时,向不存在的属性赋值
Object.preventExtensions(obj);
obj.y = 10;
// 2、当对象是不可删除的属性(isSealed或isFrozen为真时),尝试删除属性
Object.seal(obj);
delete obj.x;
// 3、删除某些不能删除的系统属性、标识符,或configurable性质为false的属性
delete Function.prototype;
// 4、写只读属性
Object.defineProperty(obj, 'x', {writable: false});
obj.x = 200;
// 5、写常量
const a = '123';
a = 'q';
// 上述对属性的操作中,由于属性的性质时可以在运行期改变的
// 因此在语法分析并不能确认上述操作是否有效
```
###### 3、访问arguments.callee或函数的caller属性将导致“类型异常”
```javascript
// 严格模式下会报执行异常
function fn(){
console.log(typeof arguments.callee)
console.log(typeof arguments.callee.caller)
console.log(typeof fn.caller)
}
// 这里的异常时由属性存取运算符导致的,严格模式下运算符认为callee\caller是不正确的
function fn2(){
console.log('caller' in fn2); // => true
console.log('callee' in arguments); // => true
}
```
###### 4、以下代码的执行效果与非严格模式并不一致
```javascript
// 严格模式下会报执行异常
// 对arguments[n]与形式的修改将不再相互影响
// 在严格模式中返回传入的x的值
// 在非严格模式下返回100
function fn(x){
arguments[0] = 100;
return x;
}
```
### 严格模式限制的范围
除非在创建和启动js引擎时将它置为严格模式,或者通过模块来加载整个系统,否则默认情况下用户代码只能指定一个有限范围的严格模式
#### 有限范围下的严格模式
###### 1、如果一段代码被标志为“严格模式”,则其中运行的所有代码都必然是严格模式下的(类似于全局作用域)
###### 2、如果在某个函数内部加入严格模式,它不会影响到外面的代码(类似局部作用域)
###### 3、如果“指示前缀”出现在代码中间,作为一个字面量表达式语句时,那么它将会被忽略,也不会导致代码块进入严格模式
#### 严格模式下动态执行非严格模式的全局环境
在任何处于严格模式的代码中,js引擎都允许用户代码通过以下两种方式将代码执行在一个非严格模式的全局环境中
###### 1、使用间接调用的eval()函数
###### 2、使用new Function方式创建的函数