JavaScript ES6模块:避免污染全局命名空间

4

背景

在JavaScript中导入模块时,我们会使用导入模块的名称来污染全局命名空间:

foo.js

export foo() {..};
export const bar = 3.14;

index.js

import { foo, bar } from './foo.js';

问题

index.js 中,foobar 存在于全局命名空间中,对吗?那么,假设我发布此模块,并有人将它与另一个脚本一起在其 HTML 文件中使用,该脚本还在全局命名空间中定义了变量 foobar。那么我们就会有冲突了吗?

我猜这可以通过在一个 IIFE 中包装 main.js 中的所有内容来解决。但是,由于某种原因,ESLint 对此提出了异议,这让我想知道是否IIFE不是保护全局命名空间的首选/推荐方法。

  1. 全局命名空间是否会被污染以及 foobar 是否会产生冲突?
  2. 如果是,我应该如何避免?

谢谢。


你是否使用任何类型的打包工具?如果是,它们将每个文件视为IIFE,因此foobar不会放在全局作用域中。但是,如果没有使用打包工具,那么Chris G所说的是正确的。 - mhodges
当我尝试将所有内容包装在IIFE中时,ESLint似乎没有抱怨。 - Travis J
@mhodges 嗯,我不知道那个。你有 webpack 规范的链接吗?有时候我只用 Gulp 和 babel,所以没有打包工具。但是对于更大的应用程序,我会使用 webpack。 - Magnus
1
使用 https://eslint.org/demo,选择 ES6,我使用了 (function(){ let foo = 5; const bar = 3.14; if(foo) foo += bar; })() 并得到了 "无需 Lint!"。 - Travis J
@TravisJ 尝试使用AirBnB配置的ESLint。实际上,我刚刚注意到,在IIFE / import上出现了新的ESLint错误消息:“解析错误:'import'和'export'只能出现在顶层”。 - Magnus
显示剩余6条评论
1个回答

10
当在JavaScript中导入模块时,我们会使用导入的模块名称污染全局命名空间。
不是这样的。每个模块都有自己的模块作用域,在其中存储所有导入的绑定和顶层声明。
在仅使用ES6模块的普通ES6环境中,几乎从不使用全局作用域——所有模块代码都是严格模式代码,因此您必须努力才能在全局对象上创建变量。
模块捆绑器通常通过允许您声明一些导出来成为打包脚本中的全局变量来缓解这种情况,以便在使用其他脚本时可以轻松访问它们。

啊,有趣。只是为了百分之百确定我理解你的意思:当我导入一个模块(ES6风格),它仅在导入它的.js文件中可访问。导入的模块名称不会与其他脚本中相同的名称发生冲突? - Magnus
是的,导入语句是在局部作用域中创建的。与脚本不同,每个模块都有自己的隐式作用域。 - Bergi
模块之间唯一可能发生冲突的是模块名称(通常是文件路径)本身。导出与各个模块相关联。 - Bergi
明白了。对我来说这有点反直觉,因为我在index.js中访问了foobar,就像它们是全局变量一样。注意:index.js本身不是一个模块,它是直接连接在index.html中的顶层脚本。我想我们可以把index.js看作被封装在IIFE中,即使它并不是? - Magnus
2
它们是指向 foo.js 模块中变量的绑定 - 只有在导入它们后才可用。不,index.js 也是一个模块,只有模块可以使用 import 语法。你可能无法在其中创建 var global。当然,你的打包器/转译器会将所有内容放在一个由 IIFE 包装的脚本中。 - Bergi
作为后续行动,我尝试使用require.js模块加载器(AMD模块系统)创建模块,结果发现顶级模块未被封装,它们位于全局范围内。请参考以下CodePen:https://codepen.io/magnusriga/pen/wjZLPg - Magnus

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接