Why does babel rewrite imported function call to (0, fn)(...)?
The comma operator evaluates each of its operands (from left to right) and returns the value of the last operand.
console.log((1, 2)); // Returns 2 in console
console.log((a = b = 3, c = 4)); // Returns 4 in console
So, let see an example:
var a = {
foo: function() {
console.log(this === window);
}
};
a.foo(); // Returns 'false' in console
(0, a.foo)(); // Returns 'true' in console
Now, in foo
method, this
is equal to a
(because foo
is attached to a
). So if you call a.foo(
) directly, it will log false
in console.
But, if you were call (0, a.foo)()
. The expression (0, a.foo)
will evaluate each of its operands (from left to right) and returns the value of the last operand. In other words, (0, a.foo)
is equivalent to
function() {
console.log(this === window);
}
Since this function no longer is attached to anything, its this
is the global object window
. That's why it log true
in console when call (0, a.foo)()
.
Calling a function in this roundabout way:
(throwAwayValueHere, fn)(args);
works like this:
- The comma expression
throwAwayValueHere, fn
is evaluated: The comma operator evaluates its first operand, throws away that value, then evaluates its second operand and takes that value as its result. - That value is then called as a function, passing in the arguments.
Doing the call that way has an effect in two situations:
1. If the function is on an object property, e.g.:
(throwAwayValueHere, obj.fn)(args);
it calls the function without setting this
to obj
during the function call; instead, it's set to the default, either the global this
value (window
on browsers) or undefined
in strict mode.
Example:
"use strict";
const obj = {
value: 42,
fn: function() {
console.log(`typeof this = ${typeof this}`);
if (typeof this === "object") {
console.log(`this.value = ${this.value}`);
}
}
};
// Normal call:
console.log(`obj.fn():`);
obj.fn();
// Indirect call:
console.log(`(0, obj.fn)():`);
(0, obj.fn)();
This is the reason Babel is doing it there: In the original code, the call was simply a()
, which calls a
with the default this
value. Doing (0, _b.a)()
does the same thing even though a
is a property of _b
.
2. If the function is eval
, it makes it an indirect eval
which means it's evaluated as though at global scope, rather than eval
's default behavior of running arbitrary code from a string in local scope, giving it access to all in-scope variables.
Example:
"use strict";
let a = "global a";
function directEval() {
let a = "local a";
eval("console.log(`a = ${a}`);");
}
function indirectEval() {
let a = "local a";
(0,eval)("console.log(`a = ${a}`);");
}
console.log("direct:");
directEval();
console.log("indirect:");
indirectEval();
(0, _b.a)()
ensures that the function _b.a
is called with this
set to the global object (or if strict mode is enabled, to undefined
). If you were to call _b.a()
directly, then _b.a
is called with this
set to _b
.
(0, _b.a)();
is equivalent to
0; // Ignore result
var tmp = _b.a;
tmp();
(the ,
is the comma operator, see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comma_Operator).