前端开发的第三年,突然发现,对于JS,我还有很多不懂的地方,趁着最近需求少,不如静下心来,从头把JS再学一遍,查漏补缺。 本系列以廖雪峰的《JavaScript教程》和《现代 JavaScript 教程》两个电子书作为线索,对其中需要进一步了解的知识,会阅读更多的文章,并作为扩展知识记录下来。 新手建议先阅读上面两个电子书,本系列更适合用来复习旧知识和查漏补缺。
"use strict"出现的原因
ES旧标准中,存在不完善的特性,ES5规范对这些不完善的特性进行了修改。
但是为了兼容老版本浏览器,这些不完善的特性还是默认可以使用。
为了使用新特性,避开老版本中这些不完善的特性,我们可以开启严格模式“use strict”。
简单来说,严格模式的作用就是:
- 消除ES旧标准中不合理的特性。
- 保证代码的安全和准确。
- 利于JS引擎执行优化,提升运行速度。
"use strict"如何开启
要开启严格模式,我们可以把"use strict"放在文件顶部或者函数顶部。
如果代码用了诸如 "classes" 和 "modules"这些新标准的特性,也会自动开启严格模式。
"use strict"带来什么变化
如果开启了严格模式,我们的代码会有什么样的变化呢?
这些变化,我们只要理解是为了填旧版本留下来的坑,就能很快记住,大概情况可以先看下这张图,下文会再给出具体的例子。
具体的说明参考严格模式 MDN,进一步理解,可以看JS 中的严格模式【 经典前端面试题 】这个视频,下面的例子将再进一步方便大家理解。
将过失错误转成异常
无法意外创建全局变量
代码语言:javascript复制// 创建一个全局变量叫做message
message = "Hello JavaScript! "; // 这一行代码就会抛出 ReferenceError
静默失败的赋值操作也抛出异常
代码语言:javascript复制"use strict";
// 给不可写属性赋值
var obj1 = {};
Object.defineProperty(obj1, "x", { value: 42, writable: false });
obj1.x = 9; // 抛出TypeError错误
// 给只读属性赋值
var obj2 = { get x() { return 17; } };
obj2.x = 5; // 抛出TypeError错误
// 给不可扩展对象的新属性赋值
var fixed = {};
Object.preventExtensions(fixed);
fixed.newProp = "ohai"; // 抛出TypeError错误
删除不可删除熟悉抛出异常
代码语言:javascript复制"use strict";
delete Object.prototype; // 抛出TypeError错误,非严格模式时,无法删除但不报错
属性名唯一、函数参数名唯一
代码语言:javascript复制"use strict";
var o = { p: 1, p: 2 }; // !!! 语法错误,非严格模式时,直接覆盖前面的同名属性
function sum(a, a, c) { // !!! 语法错误
return a a c; // 代码运行到这里会出错,非严格模式时,直接覆盖前面的同名参数
}
禁止八进制数字语法
代码语言:javascript复制"use strict";
var sum = 015 // !!! 语法错误,因为8进制和补零会冲突
197
142;
代码语言:javascript复制var a = 0o10; // ES6: 八进制,用0o就没问题
禁止设置原始数据的属性
代码语言:javascript复制(function() {
"use strict";
false.true = ""; //TypeError
(14).sailing = "home"; //TypeError
"with".you = "far away"; //TypeError
})();
简化变量的使用
禁用with
代码语言:javascript复制"use strict";
var x = 17;
with (obj) { // !!! 语法错误
// 如果没有开启严格模式,with中的这个x会指向with上面的那个x,还是obj.x?
// 如果不运行代码,我们无法知道,因此,这种代码让引擎无法进行优化,速度也就会变慢。
x;
}
eval不再为上层范围引入新变量
在 eval 执行的严格模式代码下,变量的行为与严格模式下非 eval 执行的代码中的变量相同。
代码语言:javascript复制var x = 17;
var evalX = eval("'use strict'; var x = 42; x"); //严格模式外部x不会被赋值为42,非严格模式会
console.assert(x === 17);
console.assert(evalX === 42);
禁止删除声明变量
代码语言:javascript复制"use strict";
var x;
delete x; // !!! 语法错误
eval("var y; delete y;"); // !!! 语法错误
让eval和argument变得简单
eval和arguments不能作为变量名
代码语言:javascript复制"use strict";
eval = 17; // !!! 语法错误
arguments ; // !!! 语法错误
arguments形参和实参没有映射关系
代码语言:javascript复制function f(a) {
"use strict";
a = 42;
return [a, arguments[0]];
}
var pair = f(17);
console.assert(pair[0] === 42);
console.assert(pair[1] === 17); //严格模式下arguments[0]没有映射关系,还是17,非严格模式下会被改成42
不允许使用arguments.callee
代码语言:javascript复制"use strict";
var f = function() { return arguments.callee; }; //正常模式下,arguments.callee 指向当前正在执行的函数,即f本身。
f(); // 抛出类型错误
更容易写出安全的代码
this传递给一个函数的值不会被强转成一个对象, 默认是undefined
代码语言:javascript复制"use strict";
function fun() { return this; }
console.assert(fun() === undefined); //非正常模式下,this===window
不能使用caller和arguments访问上层函数和调用本层函数时候的形参
代码语言:javascript复制function restricted() {
"use strict";
restricted.caller; // 抛出类型错误
restricted.arguments; // 抛出类型错误
}
function privilegedInvoker() {
return restricted();
}
privilegedInvoker();
不会再提供访问与调用这个函数相关的变量的途径
代码语言:javascript复制"use strict";
function fun(a, b) {
"use strict";
var v = 12;
return arguments.caller; // 抛出类型错误
}
fun(1, 2); // 不会暴露v(或者a,或者b)
为未来的新特性铺路
预留关键字
包括implements
, interface
, let
, package
, private
, protected
, public
, static
和yield
这些关键字。
禁止不在脚本和函数层面上的函数声明
代码语言:javascript复制"use strict";
if (true) {
function f() { } // !!! 语法错误
f();
}
for (var i = 0; i < 5; i ) {
function f2() { } // !!! 语法错误
f2();
}
function baz() { // 合法
function eit() { } // 同样合法
}
参考资料:
- 严格模式 MDN
- 现代 JavaScript 教程 — "use strict" 现代模式
- JS 中的严格模式【 经典前端面试题 】