如何阻止Babel将"this"转换为"undefined"(并插入"use strict")

56
EDIT: 这不是关于箭头函数的问题。它也不是关于将 this 传递给IIFE 。这是一个与转译器相关的问题。
因此,我为我正在开发的应用程序创建了一个简单的发布-订阅模式。我使用ES6编写它,以使用spread/rest并避免一些麻烦。我使用npm和gulp进行转译,但它让我发疯了。
我将其设置为浏览器库,但意识到它可以在任何地方使用,因此我决定使其Commonjs和AMD兼容。
以下是我的代码缩减版本:
(function(root, factory) {
 if(typeof define === 'function' && define.amd) {
    define([], function() {
        return (root.simplePubSub = factory())
    });
  } else if(typeof module === 'object' && module.exports) {
    module.exports = (root.simplePubSub = factory())
  } else {
    root.simplePubSub = root.SPS = factory()
  }
}(this, function() {
 // return SimplePubSub
});

但无论我尝试什么(例如将 this 设置为变量并传递),它都将其设置为未定义

}(undefined, function() {

可能与Babel不知道this将是什么有关,所以将其转译掉了,但我还能采取其他方法吗?

更新:传递}((window || module || {}), function() {而不是this似乎可行。虽然我不确定这是最好的方法。


2
那么,在这种情况下,this 等于 undefined,这意味着两者是相同的。"不知道 this 将会是什么" --- 它和每个人都知道它将是 undefined,根据标准。 - zerkms
2
@zerkms 在浏览器中,this === window,而在 Node 中,在全局范围内使用时 this === module(好吧,就像一个 Node 模块可以是全局的一样)。 - Alexander O'Mara
1
@AlexanderO'Mara 嗯,并非在每种情况下都是如此。提示:严格模式怎么样? - zerkms
1
@zerkms 说控制台确实在全局范围内运行,但你可以自己看一下:https://rawgit.com/AlexanderOMara/b8a118613be73545c1ea/raw/b0a227bbfeeaf9ae97a378d1ebd5966e4fc82247/index.html - Alexander O'Mara
@AlexanderO'Mara 它是[object Window]。它是IE 11.0.9600.18163在Windows 7 x64下。无论如何,让我们在SO上提出一个适当的问题,等一下... http://stackoverflow.com/q/34973752/251311 - zerkms
显示剩余18条评论
2个回答

75

对于 Babel >= 7.x

ES6代码有两种处理模式:

  • "script" - 当您通过<script>或任何其他标准的ES5文件加载方式加载文件时
  • "module" - 当文件被处理为ES6模块时

在Babel 7.x中,默认情况下将文件解析为"module"。导致您遇到麻烦的是,在ES6模块中,thisundefined,而在"script"情况下,这取决于环境,如浏览器脚本中的window或CommonJS代码中的exports。同样,"module"文件会自动变成严格模式,因此Babel会插入"use strict";

在Babel 7中,如果您想避免这种行为,您需要告诉Babel文件的类型。最简单的选项是在Babel选项中使用"sourceType"选项将sourceType: "unambiguous"设置为基本上告诉Babel根据importexport语句的存在来猜测类型(脚本还是模块)。主要缺点是,拥有不使用importexport的ES6模块是技术上可以的,但那些会被错误地视为脚本。另一方面,那确实不太常见。

另外,您可以使用Babel 7的"overrides"选项将特定文件设置为脚本,例如:

overrides: [{
  test: "./vendor/something.umd.js",
  sourceType: "script",
}],

任何一种方法都可以让Babel知道某些文件是“script”类型,因此不应将“this”转换为“undefined”。
对于Babel < 7.x:
ES6代码有两种处理模式:
- “script”-通过< script >或任何其他标准的ES5方式加载文件时 - “module”-当文件被处理为ES6模块时
在使用Babel 6和babel-preset-es2015(或Babel 5)时,默认情况下,Babel假定它处理的文件是ES6模块。导致问题的原因是,在ES6模块中,“this”是“undefined”,而文件都是严格模式,而在“script”情况下,“this”取决于环境,例如浏览器脚本中的“window”或CommonJS代码中的“exports”。
如果您正在使用Babel,则最简单的方法是在不使用UMD包装器的情况下编写代码,然后使用类似Browserify的工具捆绑文件,自动为您添加UMD包装器。Babel还提供了babel-plugin-transform-es2015-modules-umd。两者都旨在简化操作,因此如果您想要自定义UMD方法,则可能不适用。
或者,您需要在babel-preset-es2015中显式列出所有Babel插件,确保排除模块处理babel-plugin-transform-es2015-modules-commonjs插件。请注意,这也会停止自动添加use strict,因为这也是ES6规范的一部分,您可能需要添加babel-plugin-transform-strict-mode以自动保持代码严格性。
从babel-core@6.13开始,预设可以带有选项,所以您也可以这样做。
{
  "presets": [
    [ "es2015", { "modules": false } ]
  ]
}

在您的Babel配置文件(.babelrc)中使用禁用模块处理的babel-preset-es2015


谢谢!这使得它有意义。我选择传递所有可能的_this_,但我可能会根据你的建议重新考虑。 - JR Halchak
2
然后使用babel-preset-es2015-script代替babel-preset-es2015babel-preset-es2015-script已经排除了babel-plugin-transform-es2015-modules-commonjs - Illuminator
5
根据 babel-preset-es2015-script 的 GitHub 页面,该模块已被弃用,你应该改用以下内容:{ "presets": [ [ "es2015", { modules: false } ] ] }。注意不要改变原来的意思,需要使翻译更加通俗易懂。 - Stewart
刚刚随意回到这个问题并看到了更新。谢谢!那非常有帮助。 - JR Halchak
1
@Daniel 给我一分钟写一个更新,然后我们可以将你的另一个问题标记为重复。 - loganfsmyth
显示剩余8条评论

3
"es2015"预设默认情况下将Babel输出包装在commonJs包装器中。使用"babel-preset-es2015-script"(你必须首先npm install --save babel-preset-es2015-script)来输出"script"(无模块)。这会对我正在使用Babel封装的其他库造成严重破坏。
预设:https://www.npmjs.com/package/babel-preset-es2015-script"

1
请注意,此方法已被弃用:https://github.com/Collaborne/babel-preset-es2015-script - Madbreaks

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