Dlaczego babel przepisuje wywołanie funkcji importowanej na (0, fn) (…)?

100

Biorąc pod uwagę plik wejściowy, taki jak

import { a } from 'b';

function x () {
  a()
}

babel skompiluje go do

'use strict';

var _b = require('b');

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

ale po kompilacji w trybie luźnym wywołanie funkcji jest wyświetlane jako _b.a();

Zrobiłem kilka badań, gdzie dodano operator przecinka, mając nadzieję, że był komentarz wyjaśniający to. Kod odpowiedzialny za jego dodanie znajduje się tutaj .

Will Smith
źródło
4
Powinni zrobić, _b.a.call()aby jasno określić zamiar.
Bergi
@Bergi Jestem pewien, że powodem tego z (0,) jest oszczędność miejsca w transpilowanym kodzie.
Andy

Odpowiedzi:

138

(0, _b.a)()zapewnia, że ​​funkcja _b.ajest wywoływana z thisustawieniem na obiekt globalny (lub, jeśli włączony jest tryb ścisły, na undefined). Jeśli miałbyś zadzwonić _b.a()bezpośrednio, _b.awywoływany jest z thisustawionym na _b.

(0, _b.a)(); jest równa

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

(the ,jest operatorem przecinka, patrz https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comma_Operator ).

Rob W
źródło
3
dzięki za link. Pominąłem to tyle razy i ostatecznie postanowiłem dowiedzieć się, co się dzieje.
theflowersoftime
@RobW Myślę, że dodanie var _a = (0, _b.a)na początku pliku, a następnie wywołanie _apozwoliłoby zaoszczędzić więcej miejsca w wielu przypadkach, jakikolwiek pomysł, że tego nie zrobili?
Andy
1
@Andy Twoja sugestia może mieć skutki uboczne, np. Kiedy _b.a(dynamiczny) getter.
Rob W
@RobW Rozumiem, więc mówisz, że pomysł polega na unikaniu potencjalnych skutków ubocznych, dopóki funkcja nie będzie musiała zostać wywołana.
Andy
Zauważ, że moduły są zawsze ścisłym kodem, więc tak jest zawsze this === undefinedi nie musisz nawet wspominać o globalnym obiekcie
Bergi
22

Operator przecinka ocenia każdy z jego operandów (od lewej do prawej) i zwraca wartość ostatniego operandu.

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

Zobaczmy więc przykład:

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

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

Teraz w foometodzie thisrówna się a(ponieważ foojest do niej przywiązana a). Więc jeśli zadzwonisz a.foo(bezpośrednio), zaloguje się falsedo konsoli.

Ale gdybyś został wezwany (0, a.foo)(). Wyrażenie (0, a.foo)oceni każdy z jego operandów (od lewej do prawej) i zwróci wartość ostatniego operandu. Innymi słowy, (0, a.foo)jest równoważne

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

Ponieważ ta funkcja nie jest już do niczego przypisana, thisjest obiektem globalnym window. Dlatego loguje się truedo konsoli, gdy dzwonisz (0, a.foo)().

Huong Nguyen
źródło
uruchomienie console.log(this === window);w konsoli programisty nie rejestruje już drukowania.
kushdilip
2
To mnie zaskoczyło. Kluczem tutaj jest to, że operator przecinka „zwraca wartość ostatniego operandu” - „wartość” jest tutaj samą funkcją, w której nie ma jej zawierającego rodzica - więc foo nie istnieje już wewnątrz a.
martinp999