likes
comments
collection
share

重学JS-1.1-知识点:严格模式“use strict”

作者站长头像
站长
· 阅读数 15

前端开发的第三年,突然发现,对于JS,我还有很多不懂的地方,趁着最近需求少,不如静下心来,从头把JS再学一遍,查漏补缺。

本系列以廖雪峰的《JavaScript教程》和《现代 JavaScript 教程》两个电子书作为线索,对其中需要进一步了解的知识,会阅读更多的文章,并作为扩展知识记录下来。

新手建议先阅读上面两个电子书,本系列更适合用来复习旧知识查漏补缺

"use strict"出现的原因

ES旧标准中,存在不完善的特性,ES5规范对这些不完善的特性进行了修改。

但是为了兼容老版本浏览器,这些不完善的特性还是默认可以使用。

为了使用新特性,避开老版本中这些不完善的特性,我们可以开启严格模式“use strict”。

简单来说,严格模式的作用就是:

  • 消除ES旧标准中不合理的特性。
  • 保证代码的安全和准确。
  • 利于JS引擎执行优化,提升运行速度。

"use strict"如何开启

要开启严格模式,我们可以把"use strict"放在文件顶部或者函数顶部。

如果代码用了诸如 "classes" 和 "modules"这些新标准的特性,也会自动开启严格模式。

"use strict"带来什么变化

如果开启了严格模式,我们的代码会有什么样的变化呢?

这些变化,我们只要理解是为了填旧版本留下来的坑,就能很快记住,大概情况可以先看下这张图,下文会再给出具体的例子。

重学JS-1.1-知识点:严格模式“use strict”

具体的说明参考严格模式 MDN,进一步理解,可以看JS 中的严格模式【 经典前端面试题 】这个视频,下面的例子将再进一步方便大家理解。

将过失错误转成异常

无法意外创建全局变量

// 创建一个全局变量叫做message
message = "Hello JavaScript! "; //  这一行代码就会抛出 ReferenceError

静默失败的赋值操作也抛出异常

"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错误

删除不可删除熟悉抛出异常

"use strict";
delete Object.prototype; // 抛出TypeError错误,非严格模式时,无法删除但不报错

属性名唯一、函数参数名唯一

"use strict";
var o = { p: 1, p: 2 }; // !!! 语法错误,非严格模式时,直接覆盖前面的同名属性
function sum(a, a, c) { // !!! 语法错误
  return a + a + c; // 代码运行到这里会出错,非严格模式时,直接覆盖前面的同名参数
}

禁止八进制数字语法

"use strict";
var sum = 015 + // !!! 语法错误,因为8进制和补零会冲突
          197 +
          142;
var a = 0o10; // ES6: 八进制,用0o就没问题

禁止设置原始数据的属性

(function() {
  "use strict";

  false.true = "";              //TypeError
  (14).sailing = "home";        //TypeError
  "with".you = "far away";      //TypeError
})();

简化变量的使用

禁用with

"use strict";
var x = 17;
with (obj) { // !!! 语法错误
  // 如果没有开启严格模式,with中的这个x会指向with上面的那个x,还是obj.x?
  // 如果不运行代码,我们无法知道,因此,这种代码让引擎无法进行优化,速度也就会变慢。
  x;
}

eval不再为上层范围引入新变量

在 eval 执行的严格模式代码下,变量的行为与严格模式下非 eval 执行的代码中的变量相同。

var x = 17;
var evalX = eval("'use strict'; var x = 42; x"); //严格模式外部x不会被赋值为42,非严格模式会
console.assert(x === 17);
console.assert(evalX === 42);

禁止删除声明变量

"use strict";

var x;
delete x; // !!! 语法错误

eval("var y; delete y;"); // !!! 语法错误

让eval和argument变得简单

eval和arguments不能作为变量名

"use strict";
eval = 17; // !!! 语法错误
arguments++; // !!! 语法错误

arguments形参和实参没有映射关系

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

"use strict";
var f = function() { return arguments.callee; }; //正常模式下,arguments.callee 指向当前正在执行的函数,即f本身。
f(); // 抛出类型错误

更容易写出安全的代码

this传递给一个函数的值不会被强转成一个对象, 默认是undefined

"use strict";
function fun() { return this; }
console.assert(fun() === undefined); //非正常模式下,this===window

不能使用caller和arguments访问上层函数和调用本层函数时候的形参

function restricted() {
  "use strict";
  restricted.caller;    // 抛出类型错误
  restricted.arguments; // 抛出类型错误
}

function privilegedInvoker() {
  return restricted();
}

privilegedInvoker();

不会再提供访问与调用这个函数相关的变量的途径

"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, staticyield这些关键字。

禁止不在脚本和函数层面上的函数声明

"use strict";
if (true) {
  function f() { } // !!! 语法错误
  f();
}

for (var i = 0; i < 5; i++) {
  function f2() { } // !!! 语法错误
  f2();
}

function baz() { // 合法
  function eit() { } // 同样合法
}

参考资料: