严格模式具有一些优点,我们在很多时候也经常发现他与非严格模式的不同。
结合诸多资料简单总结一下严格模式
它相对未加限制的JavaScript的语义进行了一些修改:
- 严格模式通过抛出错误来消除了一些原有静默错误。
- 严格模式修复了一些导致 JavaScript引擎难以执行优化的缺陷:有时候,相同的代码,严格模式可以比非严格模式下运行得更快。
- 严格模式禁用了在ECMAScript的未来版本中可能会定义的一些语法。
使用严格模式,需要使用严格模式编译指示
"use strict"
支持严格模式的JS引擎会识别并启用严格模式。而不支持的仅把他当作一个字符串
也可以单独在某个函数内开启严格模式
function a (){
"use strict";
//函数体
}
因此引出一个问题,"use strict"并不严格限制一定要写在第一行。前边无产生实际运行结果的语句,"use strict"可以不在第一行
function(){
"asd";
"use strict";
a = 1;
}
在mdn中这么说
不能盲目的合并冲突代码。试想合并一个严格模式的脚本和一个非严格模式的脚本:合并后的脚本代码看起来是严格模式。反之亦然:非严格合并严格看起来是非严格的。合并均为严格模式的脚本或均为非严格模式的都没问题,只有在合并严格模式与非严格模式有可能有问题。建议按一个个函数去开启严格模式(至少在学习的过渡期要这样做).
简单来讲,对全局使用严格模式,在有的时候会出现一些意料之外的问题(亚马逊网站上出现的错误627531 - Lighnting deals on Amazon shows only closed teasers (mozilla.org))
因此,在学习时期建议在函数内单独开启严格模式
那么严格模式到底有多严格呢?
1、变量
-
严格模式下,不允许意外创建全局变量
//变量未声明 //非严格模式下:创建一个全局变量并初始化为1 //严格模式下:抛出错误 a = 1; -
严格模式下,不允许使用delete删除变量,否则会抛出错误
// delete 操作符用于删除对象的某个属性;如果没有指向这个属性的引用,那它最终会被释放。 //删除变量 //非严格模式下:允许这样,但是可能会静默失败,返回false //严格模式下:抛出错误 let color = 'red'; delete color;何谓静默失败呢?就是在很多时候,你进行了操作,但是没有任何效果,当然也没有报错,就像下边这样
/* Object.freeze() 方法可以冻结一个对象。一个被冻结的对象再也不能被修改;冻结了一个对象则不能向这个对象添加新的属性,不能删除已有属性,不能修改该对象已有属性的可枚举性、可配置性、可写性,以及不能修改已有属性的值。此外,冻结一个对象后该对象的原型也不能被修改。 */ const obj={name:"Blue"}; Object.freeze(obj); obj.name="Bluemiao"; //非严格模式下不报错,但是输出是Blue //严格模式下,抛出错误 console.log(obj.name); -
严格模式下,不能使用保留字作为变量名
//抛出错误 let this = 'Bluemiao';ES6中的保留关键字(词法文法 - JavaScript | MDN (mozilla.org))
2、对象
-
严格模式下,给只读属性的赋值会抛出错误。
//get语法将对象属性绑定到查询该属性时将被调用的函数。详见mdn "use strict"; const obj = { log:['a','b','c'], latest:get x (){ if(log.length === 1) return undefined return this.log(this.log.length - 1) } }; console.loh(obj.latest); obj.latest = 5; //会抛出错误以上代码在node.js环境下测试,但是放在浏览器控制台发现并没有什么效果
据说是因为在console中的js代码是通过
eval()来执行的也没找到太好的解释,不过我们可以使用匿名函数来避免这问题
(function() { "use strict"; const obj = { log: ['a', 'b', 'c'], get latest() { if (this.log.length === 1) return undefined return this.log[this.log.length - 1] } }; console.log(obj.latest); obj.latest = 5; })() -
严格模式下,在不可配置属性上使用delete会抛出错误错误。
"use strict"; delete Object.prototype;
-
严格模式下,给不可写属性赋值会抛出 错误。
//Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。 //默认情况下,使用 Object.defineProperty() 添加的属性值是不可修改(immutable)的,非默认情况就是writable:true "use strict"; var obj1 = {}; Object.defineProperty(obj1, "x", { value: 42, writable: false }); obj1.x = 5; -
严格模式下,给不可扩展对象的新属性赋值会抛出 错误。
//Object.preventExtensions()方法让一个对象变的不可扩展,也就是永远不能再添加新的属性。 "use strict"; var obj2 = {}; Object.preventExtensions(obj2); obj2.newProp = "no";
3、函数
3.1
-
严格模式下,函数参数名不能重复
//在非严格模式下,这种用法不算错误,函数内使用的参数以最后一个出现的为准,想访问第一个需要借助arguments访问 //抛出错误 function func (a,a,a,a){ "use strict"; console.log(a); console.log(arguments[0]); }; func(10,20,30,40);但在过程中我还发现
//只有借助arguments[0]才能访问第一个,其他方式都不好整。不过建议大家不要这么起名字 function func(a, a, a, a) { console.log(a); //40 console.log(arguments[[...arguments].indexOf(a)]); //40 }; func(10, 20, 30, 40);
-
严格模式下,像默认参数、解构操作等都会报错
function foo(a, b, c) { "use strict"; } //报错 function bar(a, b, c='d') { "use strict"; } //报错 function qux(a, b, ...c) { "use strict"; }
3.2 arguments
-
严格模式下,函数的 arguments 对象会保存函数被调用时的原始参数,arguments[i] 的值不会随与之相应的参数的值的改变而变化;同名参数的值也不会随与之相应的 arguments[i] 的值的改变而变化。简而言之,他们两个是分离的,是独立的
function sayHello (value){ "use strict"; value = "I don't want to say hello!"; //严格模式下,输出Hello world //非严格模式下,输出I don't want to say hello! console.log(arguments[0]); return [value,arguments[0]]; }; let zzr = sayHello("Hello world"); //Hello world console.log(zzr[0] === "I don't want to say hello!"); //true -
严格模式下,不再支持 arguments.callee
arguments.callee 指向当前正在执行的函数,但就目前我学的来说,感觉没啥用,因为我的函数是声明的,我在内部的调用完全可以使用函数名来实现。
或许对于箭头函数或是匿名函数有用?望大家不吝赐教。
-
严格模式下,不能操作arguments 的值
//抛出错误 "use strict"; arguments++;以下操作都不行:
使用 let 声明;
赋予其他值;
修改其包含的值,如使用++;
用作函数名;
用作函数参数名
在 try/catch 语句中用作异常名称。
3.3 eval
-
严格模式下的 eval 不再为上层范围(surrounding scope,注:包围eval代码块的范围)引入新变量。 在正常模式下, 代码eval("var x;")会给上层函数(surrounding function)或者全局引入一个新的变量x。 这意味着,eval可能引入的新变量会覆盖它的外层变量。 在严格模式下eval仅仅为被运行的代码创建变量(就是说原先是个大家长,谁家的事都要掺一脚,现在只能自己家的事管一管了), 所以eval不会使得名称映射到外部变量或者其他局部变量:// 使用 eval()创建变量 // 非严格模式:输出 10 // 严格模式:抛出 ReferenceError function doSomething(){ eval("var x = 10"); console.log(x); }; doSomething() -
严格模式下,不能操作eval 的值,与arguments相类似
4、this
-
使用函数的 apply()或 call()方法时,在非严格模式下 null 或 undefined 值会被强制转型为全局对象(浏览器中时window)。在严格模式下,则始终以指定值作为函数 this 的值,无论指定的是什么值
// 访问属性 // 非严格模式:访问全局属性,在浏览器中测试是red // 严格模式:抛出错误,因为 this 值为 null var color = "red"; function displayColor() { "use strict" console.log(this.color); } displayColor.call(null);
5、其他变化
- 严格模式下,没有with这种语法
- 严格模式下,取消了八进制字面量
注
严格模式下,不允许函数声明,除非它们位于脚本或函数的顶级,也就是说,严格模式下,在if或者for里不能声明函数了(手动增加删除线。这个并没有测试出来)
"use strict";
if(true){
function sum(){
console.log(10 + 10);
};
};
容我再去看看
参考资料
- 《JavaScript高级程序设计》
- 严格模式 mdn(严格模式 - JavaScript | MDN (mozilla.org))
- Javascript 严格模式详解 - 阮一峰的网络日志 (ruanyifeng.com)