还不知道'use strict'的作用?这篇文章给你讲清楚

前言

还记得第一次在代码里看到 'use strict' 的时候吗?

那时候跟着教程敲代码,看到文件开头总有这么一行,也不知道干什么用的,复制粘贴就完事了。直到有一天,删掉这行之后,一些"莫名其妙能跑"的代码突然就能运行了,才意识到:这玩意儿不简单

更让人好奇的是:为什么 JavaScript 需要一个"严格模式"?难道还有"随便模式"吗?这背后到底有什么故事?

这篇文章会带你深入了解严格模式的前世今生,以及它到底解决了什么问题。

目录


一个真实的困惑:为什么有些错误被"吃掉"了?

先看一段代码,你能发现问题吗?

javascript 复制代码
function calculateTotal(price, quantity) {
  totol = price * quantity;  // 注意:这里把 total 拼错了
  return totol;
}

console.log(calculateTotal(100, 5));  // 500
console.log(totol);  // 500 - WTF?

问题来了

  1. 代码能正常运行,没有报错
  2. totol 这个变量我们从来没声明过
  3. 更诡异的是,它变成了全局变量

这就是 JavaScript 早期设计的"宽容":你不声明变量?没关系,我帮你创建一个全局的

听起来很贴心?实际上是灾难的开始


JavaScript 的历史遗留问题

诞生于仓促,成长于妥协

JavaScript 诞生于 1995 年,当时 Brendan Eich 用10天设计出了这门语言。是的,你没看错,就是 10 天。

为了让它容易上手,JavaScript 做了很多"宽容"的设计:

1. 自动创建全局变量

javascript 复制代码
function test() {
  myVar = 'hello';  // 没有 var/let/const,自动变成全局变量
}
test();
console.log(myVar);  // 'hello' - 全局污染

2. 静默失败

javascript 复制代码
var obj = {};
Object.defineProperty(obj, 'name', {
  value: 'John',
  writable: false
});

obj.name = 'Mike';  // 赋值失败,但不报错
console.log(obj.name);  // 'John' - 赋值被静默忽略

3. 意外的 this 绑定

javascript 复制代码
function showName() {
  console.log(this.name);
}

var name = 'Global';
showName();  // 'Global' - this 指向了全局对象

4. 八进制字面量的陷阱

javascript 复制代码
var num = 010;  // 猜猜这是几?
console.log(num);  // 8 - 以 0 开头被当成八进制

问题的本质

这些"宽容"的设计初衷是好的,但在实际项目中带来了:

  • 调试困难:错误被静默忽略,找 bug 像大海捞针
  • 全局污染:变量满天飞,不知道哪里会被修改
  • 行为难以预测:同样的代码在不同上下文表现不一样

随着 JavaScript 项目越来越大,这些问题变得不可忍受。


严格模式的诞生:ES5 的救赎之举

2009 年的转折点

ECMAScript 5(ES5)在 2009 年发布时,语言设计者面临一个两难选择:

  1. 修复这些问题 → 破坏向后兼容性,无数老代码会崩溃
  2. 保持兼容 → 继续背着历史包袱前行

最终,他们想出了一个优雅的解决方案严格模式

严格模式就像给 JavaScript 加了一个"安全模式"开关,开启后会:

  • 把静默错误变成显式抛出
  • 禁止使用容易出错的语法
  • 为未来的语言特性保留空间

设计哲学

严格模式的设计遵循三个原则:

1. 向后兼容

  • 老代码不加 'use strict' 继续按原来的方式运行
  • 新代码主动选择加入严格模式

2. 渐进增强

  • 可以在全局启用,也可以只在函数内启用
  • 给开发者充分的选择权

3. 更安全的 JavaScript

  • 消除语言的不安全特性
  • 为优化器提供更好的优化空间

严格模式到底做了什么?

1. 禁止意外的全局变量

非严格模式

javascript 复制代码
function loose() {
  myVar = 'oops';  // 创建全局变量
}
loose();
console.log(myVar);  // 'oops'

严格模式

javascript 复制代码
'use strict';
function strict() {
  myVar = 'oops';  // ReferenceError: myVar is not defined
}
strict();

比喻:就像门禁系统,非严格模式是"没卡也能进",严格模式是"必须刷卡"。


2. 让静默失败变成抛出错误

给不可写属性赋值

javascript 复制代码
'use strict';

var obj = {};
Object.defineProperty(obj, 'x', { value: 42, writable: false });
obj.x = 9;  // TypeError: Cannot assign to read only property

删除不可删除的属性

javascript 复制代码
'use strict';

delete Object.prototype;  // TypeError: Cannot delete property

给只读的全局对象赋值

javascript 复制代码
'use strict';

undefined = 5;  // TypeError: Cannot assign to read only property
NaN = 42;       // TypeError: Cannot assign to read only property

3. 禁止重复的参数名

非严格模式(最后一个参数"赢"):

javascript 复制代码
function sum(a, a, c) {
  return a + a + c;
}
console.log(sum(1, 2, 3));  // 2 + 2 + 3 = 7

严格模式(直接报错):

javascript 复制代码
'use strict';
function sum(a, a, c) {  // SyntaxError: Duplicate parameter name
  return a + a + c;
}

4. 禁止八进制语法

非严格模式(容易误解):

javascript 复制代码
var num = 010;  // 8,不是 10
var price = 099;  // 99(因为 9 不是有效的八进制数字)

严格模式(明确报错):

javascript 复制代码
'use strict';
var num = 010;  // SyntaxError: Octal literals are not allowed

现代替代方案

javascript 复制代码
'use strict';
var num = 0o10;  // 明确的八进制语法,值为 8

5. 禁止 with 语句

with 语句看起来方便,实际上是性能杀手可读性噩梦

非严格模式

javascript 复制代码
var obj = { a: 1, b: 2 };
with (obj) {
  console.log(a);  // 1 - a 来自哪里?不确定
  console.log(b);  // 2 - 可能是 obj.b,也可能是外层的 b
}

严格模式

javascript 复制代码
'use strict';
with (obj) {  // SyntaxError: Strict mode code may not include a with statement
  // ...
}

为什么禁止

  • 变量来源不明确,难以优化
  • 引擎无法在编译时确定变量作用域

6. this 不再自动指向全局对象

非严格模式(意外的全局绑定):

javascript 复制代码
function show() {
  console.log(this);  // Window(浏览器环境)
}
show();

严格模式(undefined,更合理):

javascript 复制代码
'use strict';
function show() {
  console.log(this);  // undefined
}
show();

实际应用

javascript 复制代码
'use strict';

function Person(name) {
  this.name = name;
}

// 非严格模式:忘记 new,this 指向 window,污染全局
// 严格模式:忘记 new,this 是 undefined,立即报错
var person = Person('John');  // TypeError: Cannot set property 'name' of undefined

这个特性帮你及时发现构造函数调用错误


7. 保留未来关键字

严格模式为 JavaScript 的未来发展保留了一些关键字:

javascript 复制代码
'use strict';

var implements = 'test';  // SyntaxError
var interface = 'test';   // SyntaxError
var let = 'test';         // SyntaxError(现在已经是关键字了)
var package = 'test';     // SyntaxError
var private = 'test';     // SyntaxError
var protected = 'test';   // SyntaxError
var public = 'test';      // SyntaxError
var static = 'test';      // SyntaxError
var yield = 'test';       // SyntaxError(现在已经是关键字了)

实战:看看严格模式如何拯救你的代码

案例 1:避免变量污染

问题代码(非严格模式):

javascript 复制代码
function updateUser(user) {
  nmae = user.name;  // 拼写错误:name 写成了 nmae
  age = user.age;
  // ... 100 行代码之后
  console.log(name);  // undefined - 因为 name 没被赋值
}

updateUser({ name: 'Alice', age: 25 });
console.log(nmae);  // 'Alice' - 全局变量污染

修复后(严格模式):

javascript 复制代码
'use strict';

function updateUser(user) {
  nmae = user.name;  // ReferenceError: nmae is not defined
  // 立即发现拼写错误,不会污染全局
}

案例 2:捕获静默失败的赋值

问题代码(配置对象被意外修改):

javascript 复制代码
const CONFIG = {};
Object.defineProperty(CONFIG, 'API_KEY', {
  value: 'secret-key',
  writable: false
});

// 某个开发者在代码的其他地方
CONFIG.API_KEY = 'new-key';  // 非严格模式:静默失败
console.log(CONFIG.API_KEY);  // 'secret-key' - 赋值没生效,但没报错

// 导致 bug:开发者以为修改成功了,实际没有

严格模式(立即发现问题):

javascript 复制代码
'use strict';

const CONFIG = {};
Object.defineProperty(CONFIG, 'API_KEY', {
  value: 'secret-key',
  writable: false
});

CONFIG.API_KEY = 'new-key';  // TypeError: Cannot assign to read only property
// 立即知道这个属性是只读的

案例 3:防止 this 绑定错误

问题代码(事件处理器):

javascript 复制代码
class Counter {
  constructor() {
    this.count = 0;
  }

  increment() {
    this.count++;
  }
}

const counter = new Counter();
const btn = document.getElementById('btn');

// 常见错误:直接传递方法引用
btn.addEventListener('click', counter.increment);

// 点击按钮时:
// 非严格模式:this 指向 btn,没有 count 属性,静默失败
// 严格模式:this 是 undefined,立即报错

正确做法

javascript 复制代码
'use strict';

// 方法 1:箭头函数
btn.addEventListener('click', () => counter.increment());

// 方法 2:bind
btn.addEventListener('click', counter.increment.bind(counter));

现代开发中的严格模式

ES6 模块和类默认启用严格模式

好消息:如果你使用现代 JavaScript,很多情况下自动就是严格模式了:

ES6 模块

javascript 复制代码
// module.js
export function test() {
  // 默认严格模式,不需要 'use strict'
  x = 1;  // ReferenceError
}

ES6 类

javascript 复制代码
class MyClass {
  constructor() {
    // 类的代码默认严格模式
    x = 1;  // ReferenceError
  }
}

构建工具的处理

现代构建工具(Webpack、Vite、Rollup 等)通常会:

  • 在打包时自动添加 'use strict'
  • 或者使用 ES6 模块,天然严格模式

你还需要手动添加吗?

需要,在以下场景:

  • 传统的脚本文件(非模块)
  • Node.js 的 CommonJS 模块(不是 ES 模块)
  • 内联的 <script> 标签

不需要,在以下场景:

  • ES6 模块(import/export
  • ES6 类
  • 现代框架的组件文件(React、Vue、Svelte 等)

最佳实践

1. 新项目:默认使用严格模式

在文件顶部添加:

javascript 复制代码
'use strict';

// 你的代码

或者使用 ES6 模块(自动严格模式):

javascript 复制代码
// 不需要 'use strict'
export function myFunction() {
  // ...
}

2. 老项目:逐步迁移

不要一次性在全局启用,可能会导致大量代码报错。

推荐策略

javascript 复制代码
// 在新写的函数中启用
function newFeature() {
  'use strict';
  // 只影响这个函数作用域
}

// 老代码保持不变
function legacyCode() {
  // 继续以非严格模式运行
}

3. 配合 ESLint 使用

.eslintrc.js 中配置:

javascript 复制代码
module.exports = {
  rules: {
    'strict': ['error', 'global'],  // 要求全局严格模式
  }
};

ESLint 会检查你的代码,确保正确使用严格模式。


4. 注意兼容性

严格模式在 所有现代浏览器 中都支持(IE 10+)。

如果需要支持更老的浏览器,可以:

javascript 复制代码
(function() {
  'use strict';
  // 你的代码在 IIFE 中,不会影响外部
})();

严格模式解决了什么问题?总结

问题类型 非严格模式 严格模式 影响
未声明变量 创建全局变量 抛出 ReferenceError 防止全局污染
只读属性赋值 静默失败 抛出 TypeError 及时发现错误
删除不可删除属性 静默失败 抛出 TypeError 保护关键属性
重复参数名 最后一个生效 抛出 SyntaxError 避免混淆
八进制字面量 0 开头表示八进制 抛出 SyntaxError 防止误解
with 语句 允许使用 抛出 SyntaxError 提升性能
this 自动绑定 指向全局对象 undefined 防止意外修改全局
eval 作用域 污染外部作用域 独立作用域 更安全的 eval

深入思考:严格模式的哲学

严格模式的诞生,体现了编程语言设计的一个重要原则:

Fail fast, fail loud(快速失败,明确失败)

与其让错误静默传播 ,在几百行代码之后才爆发成难以调试的 bug,不如在错误发生的第一时间大声报错

这也是现代编程语言(TypeScript、Rust、Swift)的共同趋势:

  • 编译时捕获错误 优于 运行时调试
  • 明确的类型系统 优于 隐式转换
  • 不可变性 优于 随意修改

严格模式,是 JavaScript 向这个方向迈出的第一步。


相关资源


结语

严格模式不是 JavaScript 的"负担",而是它成熟的标志。

从 1995 年的"10 天速成",到 2009 年的严格模式,再到今天的 TypeScript、ES6+,JavaScript 一直在进化,变得更安全、更可靠、更适合构建大型应用。

'use strict' 这简单的一行代码,代表的是:

我们选择更严格的标准,因为我们要写更好的代码

下次再看到 'use strict' 时,你会知道它背后的故事,以及它默默守护你代码的方式。


相关推荐
银安3 小时前
CSS排版布局篇(2):文档流(Normal Flow)
前端·css
用户47949283569153 小时前
面试官:讲讲这段react代码的输出(踩坑)
前端·javascript·react.js
jump6803 小时前
闭包详细解析
前端
观默3 小时前
AI看完你的微信,发现了些秘密?
前端·开源
一嘴一个橘子3 小时前
useTemplateRef Vue3.5
javascript·vue.js
林希_Rachel_傻希希3 小时前
《DOM元素获取全攻略:为什么 querySelectorAll() 拿不到新元素?一文讲透动态与静态集合》
前端·javascript
PHP武器库3 小时前
从零到一:用 Vue 打造一个零依赖、插件化的 JS 库
前端·javascript·vue.js
温宇飞3 小时前
CSS 内联布局详解
前端
excel4 小时前
深入理解 Slot(插槽)
前端·javascript·vue.js