Por que o babel reescreve a chamada de função importada para (0, fn) (…)?

100

Dado um arquivo de entrada como

import { a } from 'b';

function x () {
  a()
}

babel irá compilá-lo para

'use strict';

var _b = require('b');

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

mas quando compilado em modo flexível, a chamada de função é produzida como _b.a();

Eu fiz algumas pesquisas sobre onde o operador vírgula é adicionado na esperança de que houvesse um comentário explicando isso. O código responsável por adicioná-lo está aqui .

Will Smith
fonte
4
Eles deveriam ter feito _b.a.call()para deixar a intenção clara.
Bergi
@Bergi Tenho certeza que o motivo pelo qual eles com (0,) é para economizar espaço no código transpilado.
Andy
veja também a sintaxe JavaScript (0, fn) (args)
Bergi 01 de

Respostas:

138

(0, _b.a)()garante que a função _b.aseja chamada com thisdefinido para o objeto global (ou se o modo estrito estiver ativado, para undefined). Se você chamasse _b.a()diretamente, _b.aé chamado com thisdefinido como _b.

(0, _b.a)(); é equivalente a

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

( ,é o operador vírgula, consulte https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comma_Operator ).

Rob W
fonte
3
Obrigado pelo link. passou por cima disso tantas vezes e finalmente decidiu descobrir o que estava acontecendo.
theflowersoftime
@RobW Eu acho que adicionar var _a = (0, _b.a)no início do arquivo e então chamar _aeconomizaria mais espaço em muitos casos, alguma ideia de que eles não fizeram isso?
Andy
1
@Andy Sua sugestão pode ter efeitos colaterais, por exemplo, quando _b.aé um getter (dinâmico).
Rob W
@RobW Entendo, então você está dizendo que a ideia é evitar potenciais efeitos colaterais até que a função precise ser chamada.
Andy
Observe que os módulos são sempre códigos estritos, então são sempre this === undefinede você nem precisa mencionar o objeto global
Bergi
22

O operador vírgula avalia cada um de seus operandos (da esquerda para a direita) e retorna o valor do último operando.

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

Então, vamos ver um exemplo:

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

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

Agora, no foométodo, thisé igual a a(porque fooestá anexado aa ). Portanto, se você ligar a.foo() diretamente, ele registraráfalse no console.

Mas, se você fosse chamado (0, a.foo)(). A expressão (0, a.foo)avaliará cada um de seus operandos (da esquerda para a direita) e retornará o valor do último operando. Em outras palavras, (0, a.foo)é equivalente a

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

Uma vez que esta função não está mais ligada a nada, é this é o objeto global window. É por isso que ele faz logon trueno console quando liga (0, a.foo)().

Huong Nguyen
fonte
rodando console.log(this === window);no console dev não registra mais a impressão.
kushdilip
2
Isso estava explodindo minha mente. A chave aqui é que o operador Vírgula "retorna o valor do último operando" - o "valor" aqui é a própria função sem seu pai contido - então foo não vive mais dentro de a.
martinp999