严格模式究竟限制了什么
之前就知道有严格模式的存在,但是一直没有把他整理出来,直到今天看到
<script type="module"></script>
开启了严格模式,一时说不出严格模式到底具体限制了什么东西,于是就有了这一篇
一、什么是严格模式
- 采用具有限制性
JavaScript
变体的一种方式,从而使代码隐式地脱离“马虎模式/稀松模式/懒散模式“(sloppy
)模式 - 严格模式修复了一些导致
JavaScript
引擎难以执行优化的缺陷:有时候,相同的代码,严格模式可以比非严格模式下运行得更快 - 严格模式禁用了在 ECMAScript 的未来版本中可能会定义的一些语法
不支持严格模式与支持严格模式的浏览器在执行严格模式代码时会采用不同行为。所以在没有对运行环境展开特性测试来验证对于严格模式相关方面支持的情况下,就算采用了严格模式也不一定会取得预期效果
二、如何开启严格模式
- 一种就是我们经常见的也是以前经常使用的
"use strict";
(或者'use strict';
),它需要在所有语句之前放置- 可以在全局开启严格模式
// 整个脚本都开启严格模式的语法 "use strict"; var v = "Hi! I'm a strict mode script!";
这种情况下不能盲目地合并冲突代码
- 也可以局部开启严格模式。例如:
function withStrict(){ "use strict"; var v = "Hi! I'm a strict mode script!"; }
- 另一种就是根据
ESM
,如果设置了<script type="module"></script>
,那么他就会默认开启严格模式
三、严格模式做了什么
全局变量
- 非严格模式下可以意外创建变量,挂载在全局对象上并继续工作
- 严格模式下无法再意外创建全局变量。严格模式中意外创建全局变量被抛出错误替代
静默失败
- 在非严格模式下,给只读属性赋值,给不可扩展对象添加新属性,都不会报错
- 在这里使用
Object.defineProperty
给data2
的属性name
设置为不可写 - 可以看到经过我们修改后,
name
的属性值也没有改变,表明不可写设置成功,但是我们给它进行写的操作也没有任何报错 - 我们再测试一下对不可扩展对象添加属性
- 这里采用
Object.preventExtensions()
将它设置为不可扩展 - 可以看到确实无法添加上
say
这个属性,但是也没有报错
- 在这里使用
- 同样的操作给严格模式来一遍,就会发现它是会及时抛出错误的
- 在给只读属性赋值时,抛出错误,提示了
name
属性是一个只读属性 - 在给不可扩展属性添加新属性的属性的时候,也抛出错误,提示添加属性失败,该对象为不可拓展对象
- 在给只读属性赋值时,抛出错误,提示了
函数参数
- 非严格模式下,可以给函数的形参可以重复,以最后一个重复的为主, 不过如果也可以通过
arguments
获取每个传入的实参 - 严格模式下,直接抛出错误:此上下文中不允许重复的参数名称
变量使用(with,eval)
- JS存在有些情况(
with
,eval
)会使得代码中名字到变量定义的基本映射只在运行时才产生 - 严格模式移除了大多数这种情况的发生(禁用了
with
,限制了eval
),所以编译器可以更好的优化严格模式的代码 with
: 块内的任何名称可以映射到 with 传进来的对象的属性,是在运行时决定的:在代码运行之前是无法得知的- 非严格模式下可正常使用
- 严格模式下报错
eval
:可以包含一段代码然后执行- 非严格模式下 代码
eval("var x;")
会给上层函数或者全局引入一个新的变量x
. 这意味着,一般情况下,在一个包含eval
调用的函数内所有没有引用到参数或者局部变量的名称都必须在运行时才能被映射到特定的定义 - 严格模式下,
eval
仅仅为被运行的代码创建变量,所以eval
不会使得名称映射到外部变量或者其他局部变量,也就是说它声明的变量跑不出来了
- 非严格模式下 代码
删除声明变量
- 非严格模式下,不会报错,也不会成功删除
- 严格模式下,删除声明的变量会报错
函数的arguments
-
非严格模式下,如果对
arguments
进行修改操作,也会同步到实际形参中, 也就是说arguments[i]
和其对应的参数是相互关联的,你改我也跟着改 -
严格模式下, 参数的值不会随着
arguments
对象的值改变而变化,它会保存函数调用时的原始参数,之后你对arguments
进行任何操作都不影响它 -
同时非严格模式下的
arguments.callee
指向当前正在执行的函数 -
在严格模式下,
arguments.callee
是一个不可删除属性,而且赋值和读取时都会抛出异常
this
- 在非严格模式下,对一个普通的函数来说,
this
总会是一个对象:this
它本来就是一个对象;- 还是用布尔值,字符串或者数字调用函数时函数里面被封装成对象的
this
- 还是使用
undefined
或者null
调用函数时this
代表的全局对象
- 这一点可以参考一下
call,bind,apply
实现的时候对this
的一个处理
//如果传入的为null或者undefined则指向全局对象
if(thisArg === null || thisArg === undefined) {
thisArg = window
} else {
//用Object对其进行包装
thisArg = Object(thisArg)
}
- 在严格模式下通过
this
传递给一个函数的值不会被强制转换为一个对象, 你传入什么它就是什么
非严格模式下自动转化为对象的过程不仅是一种性能上的损耗,同时在浏览器中暴露出全局对象也会成为安全隐患,因为全局对象提供了访问那些所谓安全的 JavaScript 环境必须限制的功能的途径。
关键字
- 在严格模式中一部分字符变成了保留的关键字。这些字符包括
implements
,interface
,let
,package
,private
,protected
,public
,static
和yield
- 在严格模式下,你不能再用这些名字作为变量名或者形参名
四、总结
- 将一些过失错误转成异常:
- 例如意外的全局变量,修改不可修改的属性,给不可拓展的对象新增属性,函数的参数唯一
- 简化了变量的使用:
- 例如禁用了
with
,禁止eval
内变量跑出,禁止删除变量
- 例如禁用了
- 让
arguments
变得安全简单:- 例如
arguments
是调用时实参的拷贝,改变后不影响实参;不再支持arguments.callee
方便优化
- 例如
- 更安全的JS
- 例如通过
this
传递给一个函数的值不会被强制转换为一个对象
- 例如通过
- 为未来的版本铺平道路
- 例如一部分字符变成了保留的关键字
不是很全,只是整理了一些我比较想学习的
转载自:https://juejin.cn/post/7160269243102003230