news 2026/9/12 13:58:38

ESLint no-native-reassign 规则深度解析:禁止覆盖只读全局变量及弃用迁移指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
ESLint no-native-reassign 规则深度解析:禁止覆盖只读全局变量及弃用迁移指南

ESLint no-native-reassign 规则深度解析:禁止覆盖只读全局变量及弃用迁移指南

【免费下载链接】eslintFind and fix problems in your JavaScript code.项目地址: https://gitcode.com/GitHub_Trending/es/eslint

本文围绕 ESLint 核心规则no-native-reassign展开,讲解它如何防止开发者误覆写windowObjectundefined等内置全局变量,并通过源码级分析揭示其基于"只读全局变量"与"写引用"的检测原理。读完本文,你将掌握该规则的完整配置方法(含exceptions选项)、它与no-undefno-global-assign的关联脉络,以及从no-native-reassign平滑迁移到no-global-assign的实操方案。

为什么不能给内置全局变量赋值

JavaScript 运行环境内置了大量全局变量,例如浏览器中的window、Node.js 中的process。绝大多数情况下,你不应该给这些全局变量重新赋值——一旦覆盖,就可能永久丢失对关键功能的访问。例如在浏览器代码中,下面的写法几乎总是错误:

window = {};

window这种例子比较显而易见,但实际环境中内置全局对象往往数以百计,开发者很难逐一判断"我此刻赋值的名字是不是某个内置全局变量"。no-native-reassign规则正是为这个问题而生:它利用 ESLint 对全局变量"可写/只读"状态的跟踪能力,自动拦截对只读全局变量的修改。

从版本演化看,本规则从 v0.0.9 起随 ESLint 存在(见 docs/src/_data/rule_versions.json),其历史可以追溯到它原先作为no-undef规则的一部分(见 docs/src/use/migrating-to-3.0.0.md)。

规则详情:它到底检测什么

规则的核心声明是:禁止修改只读(read-only)的全局变量(原文见 docs/src/rules/no-native-reassign.md)。这里的关键在于"只读"二字——ESLint 本身具备把全局变量配置为只读的能力,规则针对的不仅是原生内置对象,也包括任何被显式声明为只读的全局变量。

源码级原理:从Program节点到Reference

打开规则实现 lib/rules/no-native-reassign.js,可以看到检测逻辑非常精巧,总共只有三个关键函数:

  1. create(context):入口。先读取配置context.options[0]中的exceptions数组(默认空数组),随后返回一个Program节点访问器。
  2. Program(node):在遍历到程序根节点时,通过sourceCode.getScope(node)拿到全局作用域(global scope),遍历其中的每个变量。
  3. checkVariable(variable):核心筛选逻辑——只有满足variable.writeable === false(即该变量是只读的)且名字不在exceptions白名单中的变量才会被进一步检查;随后对其全部引用执行checkReference
  4. checkReference(reference, index, references):对每个引用,若满足reference.init === false(不是变量声明初始化)且reference.isWrite()(是一个写引用),则报告错误nativeReassign,消息为"Read-only global '{{name}}' should not be modified."

需要注意checkReference中有一个处理解构赋值的细节:解构赋值可能为同一个标识符产生多个写引用,因此通过index === 0 || references[index - 1].identifier !== identifier去重,避免对同一标识符重复报告。

这套机制意味着:规则并不需要硬编码"内置对象清单",而是完全依赖 scope manager 对变量writeable状态的判定——无论该状态来自语言内置全局、/*global*/配置注释,还是配置文件中的globals设置,规则都能统一覆盖。这正是它"自动识别成百上千个内置全局"的能力来源。

测试用例印证

在 tests/lib/rules/no-native-reassign.js 中可以看到规则的完整行为边界:

  • 变量声明不会被误报"string = 'hello world';""var string;"是合法代码——因为普通变量的writeable不为false
  • 内置对象赋值报错"String = 'hello world';""Array = 1;"均产生nativeReassign错误;
  • 自增/自减也是写操作"String++;"同样报错,印证isWrite()会把++/--视为写引用;
  • 解构赋值默认值会被检测"({Object = 0, String = 0} = {});"(ES6)产生两个错误;
  • 只读全局的任意赋值形式都报错/*global b*/ b = 1;function f() { b = 1; }(配合globals: { b: false })都会被拦截。

错误示例

以下代码会触发no-native-reassign报告。错误(对内置全局赋值):

/*eslint no-native-reassign: "error"*/ Object = null undefined = 1

错误(对显式声明为只读的全局变量赋值):

/*eslint no-native-reassign: "error"*/ /*global window:readonly*/ window = {}

通过/*global window:readonly*/配置注释,window被标记为只读,对其赋值同样被拦截。

正确示例

正确(对普通变量或自定义全局赋值不受影响):

/*eslint no-native-reassign: "error"*/ a = 1 var b = 1 b = 2

正确(声明为可写(writable)的全局变量允许赋值):

/*eslint no-native-reassign: "error"*/ /*global onload:writable*/ onload = function() {}

选项:exceptions 白名单

规则接受exceptions选项,用于指定一组"允许被重新赋值"的内置对象名:

{ "rules": { "no-native-reassign": ["error", {"exceptions": ["Object"]}] } }

配置后,Object = 0;不再报错(tests/lib/rules/no-native-reassign.js 中的合法用例正是{ code: "Object = 0;", options: [{ exceptions: ["Object"] }] })。从源码 lib/rules/no-native-reassign.js 的 schema 定义可见:exceptions必须是字符串数组,且uniqueItems: true(不允许重复项),并且additionalProperties: false——也就是说,除了exceptions之外不接受任何其他配置属性。

与全局变量配置体系的配合

规则检测能力依赖 ESLint 的全局变量声明机制,完整的声明方式见 docs/src/use/configure/language-options.md 的 "Specify Globals" 一节,主要有两种途径:

方式一:源码内配置注释

/* global var1, var2 */ // 声明两个全局变量 /* global var1:writable, var2:writable */ // 声明并允许写入

其中:readonly标记只读,:writable标记可写。基于历史原因,布尔值false与字符串"readable"等价于"readonly",布尔值true与字符串"writeable"等价于"writable",但这些旧写法已不推荐使用。

方式二:配置文件(flat config)

// eslint.config.js import { defineConfig } from "eslint/config"; export default defineConfig([ { languageOptions: { globals: { var1: "writable", var2: "readonly", }, }, }, ]);

这样var1允许被覆盖,var2则被禁止覆盖,配合no-native-reassign即可对自定义只读全局形成保护。另外,languageOptions.globals支持设置为"off"来禁用某个全局(例如Promise: "off")。对于浏览器、Node 等环境的预置全局集合,可以借助globals包(如globals.browser)按环境批量启用,测试用例中onload = 0(配合globals.browser)合法、top = 0(同样配合globals.browser)非法的对照,正是环境全局集与只读状态协同工作的直接体现。

与相关规则的协作

  • no-undefno-native-reassign历史上曾作为no-undef的一部分被拆分出来(见 docs/src/use/migrating-to-3.0.0.md)。no-undef负责报告未定义变量的使用,而no-native-reassign负责报告对只读全局的覆写,两者职责互补;no-undef文档明确建议"若同时想禁止这类赋值,参见 no-global-assign"。
  • no-extend-native:禁止扩展原生对象(如给Object.prototype添加属性),与本规则分别从"改引用"与"改原型"两个角度保护内置对象。
  • no-redeclareno-shadow:分别防止重复声明变量与变量遮蔽,与只读全局保护共同构成全局命名空间的多重防线。

规则已弃用:迁移到 no-global-assign

重要提示no-native-reassign从 ESLint v3.3.0 起已被标记为弃用(deprecated),理由是"规则更名"(Renamed rule),官方替换规则为no-global-assign,并计划在 v11.0.0 移除(见 docs/src/_data/rules_meta.json 与 docs/src/_data/rules.json 中的deprecated元数据)。在新项目中应直接使用no-global-assign

对比两份实现 lib/rules/no-native-reassign.js 与 lib/rules/no-global-assign.js,可以发现两者逻辑几乎完全一致(同样的ProgramcheckVariablecheckReference结构),区别仅在于:

  • 规则 ID 与消息 ID 不同(nativeReassignvsglobalShouldNotBeModified);
  • no-global-assign使用defaultOptions: [{ exceptions: [] }]声明默认选项,代码更简洁;
  • no-global-assign被标记为recommended: true(推荐开启),而no-native-reassignrecommended: falsefrozen: true标记不再演进。

迁移时只需把配置中的规则名替换并保持相同的exceptions配置即可:

{ "rules": { "no-global-assign": ["error", {"exceptions": ["Object"]}] } }

对应规则文档见 docs/src/rules/no-global-assign.md,其测试集 tests/lib/rules/no-global-assign.js 覆盖了与旧规则完全一致的合法/非法用例(包括解构默认值、自增自减、CommonJS 下的require、配置注释与languageOptions.globals两种声明方式等场景)。

何时不使用此规则

如果你正在有意识地覆盖某个原生对象(例如在 polyfill 或测试桩(mock)场景中替换ObjectArray等内置对象),则应关闭该规则,或通过exceptions选项显式放行这些名字。此时建议在配置中显式注明原因,避免后续维护者误解。

小结

no-native-reassign是 ESLint 保护运行时全局命名空间的经典规则:它不维护硬编码清单,而是借助 scope manager 对全局变量writeable状态的跟踪,配合isWrite()写引用检测,精准拦截对只读全局的一切修改形式(赋值、自增自减、解构默认值)。理解它的实现与配置,也就理解了其继任者no-global-assign的底层机制——在迁移到后者后,同样可以利用exceptions/*global*/注释与languageOptions.globals构建完整的全局变量防护体系。

【免费下载链接】eslintFind and fix problems in your JavaScript code.项目地址: https://gitcode.com/GitHub_Trending/es/eslint

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/12 13:56:49

Python核心技术与实战 免费下载指南:Python 新手的完整学习规划

Python核心技术与实战 免费下载指南:Python 新手的完整学习规划 【免费下载链接】geektime-books :books: 极客时间电子书 项目地址: https://gitcode.com/GitHub_Trending/ge/geektime-books 很多人学 Python,卡就卡在这一步——视频课东一榔头西…

作者头像 李华
网站建设 2026/9/12 13:55:51

基于Spring Boot与微信小程序的天文科普系统设计与实现

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/12 13:54:21

CookLikeHOC 菜品复刻指南:蚕豆炒鸡蛋的标准化配比与分步火候解析

CookLikeHOC 菜品复刻指南:蚕豆炒鸡蛋的标准化配比与分步火候解析 【免费下载链接】CookLikeHOC 🥢像老乡鸡🐔那样做饭。已添加2026年发布的《老乡鸡菜品溯源报告 2.0中新出现的菜品。主要部分于2024年完工,非老乡鸡官方仓库。文字…

作者头像 李华
网站建设 2026/9/12 13:53:22

Windows原生轴承故障诊断平台:CWRU数据集一键部署

简介:本资源是一个基于西储大学轴承数据集构建的故障诊断仿真平台,面向机械故障诊断、信号处理与Python GUI开发初学者及研究者,旨在帮助用户快速上手轴承故障分类建模与可视化分析。平台采用PySide2开发,集成数据预处理、特征提取…

作者头像 李华
网站建设 2026/9/12 13:53:18

AI内容检测规避:原理、工具与实战技巧

1. 项目背景与核心需求 在内容创作领域,AI生成内容的泛滥已经成为一个显著问题。许多平台开始部署AI检测工具来识别和过滤这类内容,导致大量创作者面临内容被标记甚至下架的风险。这种现象催生了一个新兴需求——如何有效降低文本的"AI率"&…

作者头像 李华