【问题标题】:Namespacing with IIFE in ES6?在 ES6 中使用 IIFE 命名空间?
【发布时间】:2015-12-21 04:29:04
【问题描述】:

显然,ES6 doesn't need namespacing 因为每个文件都是一个单独的模块。

那么,我该如何避免全局命名空间干扰呢?

例如,Babel 编译我的scripts/main.js 文件,只需将const 替换为var

var alert = 'This line doesn\'t do anything.'
window.alert(alert)

带有 IIFE 的命名空间(下面命名为 ANS)可防止名称冲突:

const ANS = (function () {
  const alert = 'This works'
  window.alert(alert + '.')
  return {alert: alert + ' too.'}
})()
alert(ANS.alert)

将属性添加到命名空间 ANS 比将它们添加到全局命名空间 windowthis 更干净。并且,IIFE 提供了进一步的封装。

那么,第二种方式(即使用 IIFE 创建自定义命名空间)不是比第一种更好吗?如果是这样,在 ES2015 中有更新/更好的方法吗?为什么 Babel 不为我做这个?

【问题讨论】:

  • 为什么覆盖这样的全局变量是您必须担心的事情?反正你也可以alert()
  • @loganfsmyth 因为Global Object window 有很多属性。我的应用程序也会有一些,我想避免潜在的名称冲突。请查看我更新的问题。
  • 也许在这个问题上有一些误解。您在第一个示例中是否关注alert === window.alert?如果是这样,那么在真正的 ES6 环境中情况并非如此,尽管它可能使用转译器。将 ES6 模块与适当的模块捆绑器一起使用时,绝对不是这种情况。
  • @loganfsmyth 这是有道理的。谢谢你。在关闭body 标记之前,我的index.html 引用了Babelscripts/main.js 编译。 Babel 不会为我将我的代码包装在 IIFE 中。我更新了我的问题。

标签: namespaces global-variables ecmascript-6 babeljs iife


【解决方案1】:

但是,如何防止在模块内覆盖全局变量?

你不能。与 ES5 相比,这方面没有任何变化。建议仍然是:避免使用全局变量。

【讨论】:

  • 谢谢。那么,我还应该创建一个带有 IIFE 的命名空间吗?查看我更新的问题。
  • IIFE 主要用于创建范围。模块已经有自己的作用域,因此不需要在模块中使用 IIFE(当然,除非您想在模块本身内这样做)。如何在模块中/从模块中组织或公开数据取决于您。
【解决方案2】:

显然,ES6 不需要命名空间,因为每个文件都是一个单独的模块。

不完全是。每个模块都有自己的范围,这是正确的,但不是每个文件都是一个模块。 ES6 中仍有一些脚本与 ES5 中的脚本一样工作,并且在全局范围内执行。
在这些脚本中,您仍然需要尽可能避免使用全局变量,通常是通过不声明/分配任何变量或将“模块”包装在 IEFE 中为其提供单独的变量范围。

在 ES6 中有更新/更好的方法吗?

您可以使用块和词法变量声明(letconstfunction):

{
    const msg = 'This line doesn\'t do anything.'
    window.alert(msg);
}
// msg is not defined here

或者你可以在你的IEFE中使用一个箭头函数,它允许你使用this来引用全局对象而不需要use .call(this)):

(() => {
    var msg = 'This line doesn\'t do anything.'
    window.alert(msg);
})();

但是,如何避免全局命名空间干扰或名称冲突?

在 ES6 模块中,除了内置对象和可能的全局对象之外,没有什么是全局的。避免修改它们。

当然,您需要注意模块名称之间的冲突 - 如何做到这一点应该在文档中解释模块加载器的解析器机制。

【讨论】:

  • 我的理解是 ES6 中的脚本有一个共享的词法环境,所以虽然 var alert = 'blah'; 将等同于 window.alert = 'blah';let alert = 'blah'const alert = 'blah;` 将创建一个全局词法范围的变量不改变window.alert。但更难转换。
  • 你们说得有道理,但是如果我使用 const 或块,Babel 不会将我的代码包装在 IIFE 中。
  • @MattDiPasquale:显然是因为 babel 只编译模块,而不是脚本:issue 1049
  • @Bergi 哦,谢谢!那么,我应该用什么来编译脚本呢?
  • 您是否有理由要避免使用模块?大多数人使用 Webpack 或 browserify 将他们所有的模块代码捆绑到一个脚本中。
【解决方案3】:

显然,ES6 不需要命名空间,因为每个文件都是 单独的模块。

这是错误信息。据我所知,只有使用 export 的文件才是 ES6 模块。如果没有export,ES6 文件只是一个普通的脚本,它可以访问全局范围,除非它被包装在 IIFE 中或在构建过程中被转换。

修订(2016 年 7 月 12 日):显然,我用更多错误信息纠正了错误信息。正如 Kyle Simpson clarifies below,“根据当前规范,使文件成为 ES6 模块的是环境(节点、浏览器等)选择加载它的方式,而不是其内容。”

你不知道 JS:ES 和超越中,Kyle Simpson,又名@getify,陈述

“模块仍然可以访问窗口和所有“全局变量” 挂起它,只是不像词汇顶级范围。不过,你真的 如果可能的话,应该远离模块中的全局变量。”

现在回答你的问题。

[I]不是第二种方式,即创建一个自定义命名空间 IIFE,比第一个好?

这取决于您的模块运行的环境。例如node中没有Window对象;因此,在那种环境中,无需担心您的模块会污染全局命名空间。

另一方面,在浏览器中,Window 是一个全局 Web API,您的模块确实可以访问它。从技术上讲,您的模块附加到Window 的任何东西严格来说都不是全局的;但是,附加到 Window 的任何内容都非常接近全局,因此通常认为修改它是一种不好的做法,除非通过 Windowown API

[H]如何避免全局命名空间干扰?

[我]在 ES2015 中有更新/更好的方法吗?

我不知道在 ES6 中处理范围的最佳实践是什么。目前,我做了以下工作:

  • 如果一个文件是一个模块,它的顶级作用域就是文件本身,所以我不关心全局作用域。
  • 如果文件不是模块,我将其包装在 IIFE 中。

来源:You Don't Know JS: ES & Beyond 还推荐:ES6 In Depth: Modules Jason Orendorff

【讨论】:

  • 澄清:“只有使用导出的文件是 ES6 模块”。不完全是。根据当前规范,使文件成为 ES6 模块的是环境(节点、浏览器等)选择加载它的方式,而不是其内容。
  • @jfmercer,您写道..“如果文件是模块......”和“如果文件不是模块......”..请澄清究竟是什么使文件成为模块而不是一个模块?
【解决方案4】:

通过谷歌搜索同样的问题在这里偶然发现。假设您使用诸如汇总之类的模块加载器,请尝试将此行添加到您的汇总选项中:

format: 'iife'

源:https://github.com/samccone/The-cost-of-transpiling-es2015-in-2016/blob/master/rollup-plugin-babel/rollup.config.js#L9

文档:https://github.com/rollup/rollup/wiki/JavaScript-API#format

【讨论】:

    猜你喜欢
    • 2019-07-09
    • 2016-07-16
    • 2017-07-25
    • 2020-07-25
    • 2023-03-13
    • 1970-01-01
    • 2023-04-10
    • 1970-01-01
    • 2021-07-15
    相关资源
    最近更新 更多