JS严格模式:全面解析与开发实践

一、简介

​ 在某些JS代码中,开头会有一行"use strict",这表达什么意思呢?其实,“use strict” 是一种严格模式指令(Strict Mode),是采用具有限制性 JavaScript 变体的一种方式,于2009年的 ES5 规范中首次引入,并在后续规范中不断完善。

​ 严格模式对正常的 JS 语法进行了限制,如:通过抛出错误来消除了一些原有静默错误;修复了一些导致 JS 引擎难以执行优化的缺陷,使代码运行速度更快等等。当在 JS 文件或函数的开头加上该指令时,JS 引擎会以一个更严格的语法规则来解析和执行代码,捕获潜在的语法错误,”强迫“我们编写更安全、更高质量的JS代码,从而减少一些因 JS 松散特性导致的潜在错误,增加代码的健壮性和可维护性。

二、主要语法区别

1、 变量必须先声明再使用

​ 非严格模式下,如果使用了一个未声明的变量,不会报错,JS 引擎会隐式的在全局对象上创建对应的全局变量。

​ 严格模式下,该用法会被捕获,并报错,必须先声明再使用,可以减少潜在的命名冲突与误用。

"use strict";
// 严格模式下 未声明就使用变量
a = 10; // 报错:ReferenceError: a is not defined

2、 同一作用域内禁止命名重复

​ 非严格模式下,同作用域内函数参数、变量和对象属性出现重复命名时,不会报错,但后者会覆盖前者。

​ 严格模式下,同作用域内函数参数、变量和对象属性出现重复命名时,会被捕获并抛出错误,可以避免因为重复命名所带来的覆盖问题。

'use strict';
// 严格模式下 出现命名重复
function bar(a, a) {
  // 报错:SyntaxError: Duplicate parameter name not allowed in this context
}

3、 this 不再默认指向全局对象

​ 非严格模式下,在普通函数独立调用时 this 默认指向全局对象(浏览器中为 window)。

​ 严格模式下,在普通函数独立调用时 this 为 undefined,可以防止无意间修改全局对象的属性。如果函数需要访问或修改某个特定对象,就必须显式绑定(bind/apply 等)。

'use strict';
// 严格模式下 普通函数独立调用this
function bar() {
  console.log(this); // undefined
}
bar();

4、 禁止使用 with 语句

​ 非严格模式下,允许使用 with 语句简化对象属性的访问,但可能带来性能下降和作用域不明确等问题。

​ 严格模式下,with 语句被禁用,使用该语句会被捕获并抛出错误,避免作用域混乱等问题。

'use strict';
var obj = { x: 10, y: 20 };
// 严格模式下 使用with语句会报错
with (obj) {
  console.log(x + y);// 报错:SyntaxError: Strict mode code may not include a with statement
}

5、 禁止使用关键字与保留字

​ 非严格模式下,某些关键字或保留字(static/package 等)虽然不推荐使用,但执意使用,也不会报错。

​ 严格模式下,如果使用了任何关键字或保留字,都会直接被捕获并抛出错误,避免滥用关键字。

'use strict';
// 严格模式下,使用关键字
var package = "test"; // 报错:SyntaxError: Unexpected strict mode reserved word

6、 禁止对只读属性或不可配置属性进行修改

​ 非严格模式下,修改某些只读属性或不可配置属性,通常只会静默失败,不会报错。

​ 严格模式下,修改某些只读属性或不可配置属性,会被捕获并抛出错误。

var obj = {};
Object.defineProperty(obj, 'x', {
  value: 10,
  writable: false, // 设置只读
  configurable: false // 设置不可配置和删除
});
// 严格模式下 修改只读属性或不可配置属性
obj.x = 20; // 报错:TypeError: Cannot assign to read only property 'x' of object '#'
 
  

7、 禁止删除不可删除的属性

​ 非严格模式下,删除不可删除的属性,通常只会静默失败,不会报错。

​ 严格模式下,删除不可删除的属性,会被捕获并抛出错误。

'use strict';
var obj = {};
Object.defineProperty(obj, 'x', {
  value: 10,
  configurable: false // 设置不可删除
});
// 严格模式下 删除不可删除的属性
delete obj.x; // 报错:TypeError: Cannot delete property 'x' of #
// 严格模式下 删除不可删除的属性
delete Object.prototype; // 报错:TypeError: Cannot delete property 'prototype' of function Object() { [native code] }
 
  

8、 限制 eval 作用域

​ 非严格模式下,如果通过 eval 编译的JS语句中定义了变量,该变量会影响到外部作用域。

​ 严格模式下,如果通过 eval 编译的JS语句中定义了变量,则该变量只在 eval 作用域内有效,外部作用域访问该变量会报错,避免了 eval 带来的命名污染问题。

'use strict';
function bar() {
  // 严格模式下 使用eval定义变量
  eval("var y = 20;");
  console.log(y); // 报错:ReferenceError: y is not defined
}
bar();

9、 禁止八进制字面量(0前缀)

​ 非严格模式下,声明前缀为0的数字,会被以八进制解析。

​ 严格模式下,声明前缀为0的数字,会被捕获并抛出错误,八进制必须使用 ES6 的 0o 前缀表示。

'use strict';
// 严格模式下声明前缀为 0 的数字
var num = 0123;// 报错:SyntaxError: Octal literals are not allowed in strict mode

// 正确写法,可用 ES6 八进制前缀 0o
var numOctal = 0o123; // 十进制 83

10、 arguments 对象与参数不再联动

​ 非严格模式下,arguments[i] 会与对应的形参值同步更新,互相联动。

​ 严格模式下,arguments[i] 会与对应的形参值相互独立,互不影响,避免拆解 arguments 时出现难以预测的行为。

'use strict';
function bar(a) {
  console.log(a);// 1
  arguments[0] = 2;// 修改arguments中的值
  console.log(a);// 1 (对应形参没有改变)
}
bar(1);

三、开发实践

1、默认严格模式

​ 在现代开发中,使用 ES6 模块(import/export)、现代框架(Vue、React)的某些打包工具配置(Webpack/Babel 等)编译打包后的代码、使用Class类声明时,默认就会启用严格模式,即使没有显式声明 “use strict” 也会以严格模式解析。

2、作用域

​ 严格模式(“use strict”) 是有作用域概念的,如果是在一个 JS 文件的开头使用,则整个文件作用域都属于严格模式;如果在一个函数的开头使用,则只有在该函数作用域属于严格模式。

3、兼容性

​ 严格模式在现代主流浏览器和Node.js环境中均得到较好支持,只在部分旧版本浏览器(IE9及以下)中不支持,会被浏览器忽略,但不会报错。

4、开发工具

​ 严格模式可以通过配置相关的 ESLint 规则(如 no-undef, no-unused-vars 等)以及Chrome DevTools的严格模式错误专用堆栈跟踪等开发工具,从而更好的发现问题。

5、检测严格模式

​ 可以通过立即执行函数访问 this 的方式检测当前作用域是否为严格模式。

const isStrict = (function() { return !this; })();

四、参考文档

MDN严格模式

ECMAScript® 2023 Language Specification: Strict Mode Code

请关注公众号,查看更多优质资源:

你可能感兴趣的:(JavaScript基础,javascript,前端,面试)