为什么 babel 将导入的函数调用重写为 (0, fn)(...)?

IT技术 javascript ecmascript-6 babeljs
2021-01-16 19:47:17

给定一个输入文件,如

import { a } from 'b';

function x () {
  a()
}

babel 会编译成

'use strict';

var _b = require('b');

function x() {
  (0, _b.a)();
}

但是在松散模式下编译时,函数调用输出为 _b.a();

我已经对添加逗号运算符的位置进行了一些研究,希望有评论对其进行解释。负责添加它的代码在这里

3个回答

(0, _b.a)()确保_b.a调用该函数this将 set 设置为全局对象(或者如果启用了严格模式,则为 to undefined)。如果您要_b.a()直接调用,则_b.a调用thisset to _b

(0, _b.a)(); 相当于

0; // Ignore result
var tmp = _b.a;
tmp();

(这,是逗号运算符,请参阅https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comma_Operator)。

@RobW我认为var _a = (0, _b.a)在文件顶部添加然后调用_a会在很多情况下节省更多空间,知道他们没有这样做吗?
2021-03-16 19:47:17
请注意,module始终是严格的代码,因此始终如此this === undefined,您甚至不需要提及全局对象
2021-03-21 19:47:17
@Andy 您的建议可能有副作用,例如何时_b.a是(动态)吸气剂。
2021-03-22 19:47:17
@RobW 我明白了,所以你说的想法是在需要调用函数之前避免潜在的副作用。
2021-03-30 19:47:17
谢谢你的链接。把这个经过了很多次,最后决定找出发生了什么。
2021-04-06 19:47:17

逗号运算符计算其每个操作数(从左到右)并返回最后一个操作数的值。

console.log((1, 2)); // Returns 2 in console
console.log((a = b = 3, c = 4)); // Returns 4 in console

所以,让我们看一个例子:

var a = {
  foo: function() {
    console.log(this === window);
  }
};

a.foo(); // Returns 'false' in console
(0, a.foo)(); // Returns 'true' in console

现在,在foo方法中,this等于a(因为foo附加到a)。所以如果你a.foo(直接调用) ,它会登录false控制台。

但是,如果你被调用(0, a.foo)()该表达式(0, a.foo)将评估其每个操作数(从左到右)并返回最后一个操作数的值。换句话说,(0, a.foo)等价于

function() {
  console.log(this === window);
}

由于此功能不再附加到任何东西,因此它this是全局对象window这就是为什么它true在调用时登录控制台的原因(0, a.foo)()

console.log(this === window);在开发控制台中运行不再记录打印。
2021-03-30 19:47:17
这让我大吃一惊。这里的关键是逗号运算符“返回最后一个操作数的值”——这里的“值”是函数本身,没有包含它的父对象——所以 foo 不再存在于 a 中。
2021-04-07 19:47:17

以这种迂回方式调用函数:

(throwAwayValueHere, fn)(args);

像这样工作:

  • 计算逗号表达式throwAwayValueHere, fn:逗号运算符计算其第一个操作数,丢弃该值,然后计算其第二个操作数并将该值作为其结果。
  • 然后将该值作为函数调用,传入参数。

以这种方式调用会在两种情况下产生影响:

1. 如果函数在对象属性上,例如:

(throwAwayValueHere, obj.fn)(args);

在函数调用期间调用函数而不设置thisobj相反,它被设置为默认值,无论是全局this值(window在浏览器上)还是undefined在严格模式下。

例子:

这就是 Babel 在那里这样做的原因:在原始代码中,调用只是a(),它a使用默认this调用(0, _b.a)()同样的事情,即使a_b.

2. 如果函数是eval,它使它成为一个间接的eval,这意味着它就像在全局范围内进行评估,而不是eval本地范围内的字符串运行任意代码的默认行为,使其可以访问所有范围内的变量。

例子: