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

2023-04-22 16:10:33 浏览数 (1)


前端开发的第三年,突然发现,对于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, staticyield这些关键字。

禁止不在脚本和函数层面上的函数声明
代码语言: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 中的严格模式【 经典前端面试题 】

0 人点赞