ES6 natychmiast wywołał funkcję strzałki


149

Dlaczego to działa w Node.jskonsoli (testowane w 4.1.1 i 5.3.0), ale nie działa w przeglądarce (testowane w Chrome)? Ten blok kodu powinien tworzyć i wywoływać anonimową funkcję, która rejestruje Ok.

() => {
  console.log('Ok');
}()

Ponadto, chociaż powyższe działa w Node, to nie działa:

n => {
  console.log('Ok');
}()

Ani to:

(n) => {
  console.log('Ok');
}()

Dziwne jest to, że kiedy parametr jest dodawany, w rzeczywistości rzuca on SyntaxErrorw natychmiast wywołującą część.


8
Dobre pytanie. Obie sparametryzowane wersje współpracują z Babel
CodingIntrigue

2
Poza zainteresowaniem, czy (n => { console.log("Ok"); })();działa?
CodingIntrigue

Tak (n => { console.log("Ok"); })()działa nawet w konsoli
deweloperskiej

i tak, 3 lata później, odpowiedź brzmi? z pewnością jedna z 3 poniższych odpowiedzi powinna zostać zaakceptowana ?!
joedotnot

@joedotnot Nie otrzymałem jasnej odpowiedzi, głównie była to dziwna implementacja w Node.js. Wygląda na to, że w najnowszej wersji Node.jspierwsza wersja już nie działa.
XCS

Odpowiedzi:


194

Musisz uczynić go wyrażeniem funkcji zamiast definicji funkcji, która nie potrzebuje nazwy i czyni ją poprawnym JavaScriptem.

(() => {
  console.log('Ok');
})()

Jest odpowiednikiem IIFE

(function(){
   console.log('Ok')
})();

Możliwym powodem, dla którego to działa w Node.js, ale nie w chrome, jest to, że jego parser interpretuje go jako funkcję samowykonującą się, ponieważ

function() { console.log('hello'); }();

działa dobrze w Node.jsTo jest wyrażenie funkcji i chrome i firefox, a większość przeglądarek interpretuje to w ten sposób. Musisz go wywołać ręcznie.

Najpowszechniej akceptowanym sposobem mówienia parserowi, że oczekuje wyrażenia funkcyjnego, jest po prostu umieszczenie go w parenach, ponieważ w JavaScript pareny nie mogą zawierać instrukcji. W tym momencie, gdy parser napotka słowo kluczowe function, wie, że należy je przeanalizować jako wyrażenie funkcji, a nie deklarację funkcji.

Jeśli chodzi o wersję sparametryzowaną , to zadziała.

((n) => {
  console.log('Ok');
})()

4
Pierwszy przykład działa w programie Node.jsi faktycznie rejestruje wartość. Moje pytanie brzmi: dlaczego to działa? A dlaczego tak się nie dzieje, gdy dodam parametr?
XCS

1
Znam się dość dobrze IIFEi wiem, jak naprawić mój kod. Byłem po prostu ciekawy, dlaczego na przykład mój IIFEnie działa po ndodaniu parametru, mimo że działał bez parametru.
XCS

3
Nie przegłosowałem, ale pytanie brzmi, dlaczego sparametryzowana wersja nie działa w Node, skoro dokładnie ta sama definicja bez parametru działa - nie pyta o różnicę między implementacjami funkcji anonimowych w Node / Chrome
CodingIntrigue

1
Warto wiedzieć, ale nie odpowiada na pytanie, jak wspomniano wcześniej, - dlaczego sparametryzowana wersja nie działa w Node, skoro działa ta sama definicja bez parametru
jkris

Ale co jest odpowiednikiem function(){}()funkcji strzałkowych? Jeśli chcę, aby wyrażenia funkcyjne uwidocznione przez obiekt funkcji chroniły przed tym.
dabadaba

18

Żadne z nich nie powinno działać bez nawiasów.

Czemu?

Ponieważ zgodnie ze specyfikacją:

  1. ArrowFunction jest wymieniony pod AssignmentExpression
  2. W LHS o CallExpression musi być MemberExpression , SuperCall lub CallExpression

Więc ArrowFunction nie może znajdować się po lewej stronie CallExpression .


Co to znaczy w jaki sposób skutecznie =>powinny być interpretowane, jest to, że działa na tym samym poziomie, co w rodzaju operatorów przypisania =, +=etc.

Znaczenie

  • x => {foo}() nie staje się(x => {foo})()
  • Tłumacz próbuje to zinterpretować jako x => ({foo}())
  • Dlatego nadal jest to błąd SyntaxError
  • Więc interpreter decyduje, że (musiał być zły i zgłasza SyntaxError

Był to błąd na Babel o tym tutaj , też.


To kilka ważnych punktów, ale jeśli spróbuję zamienić pierwszą, działającą wersję, na: () => ({console.log('Ok')}())to już nie działa. Więc tak naprawdę nie interpretuje tego w ten sposób.
XCS

@Cristy To nie jest prawidłowa produkcja funkcji strzałki. Uważa, że ​​próbujesz utworzyć obiekt z literałem Object (w nawiasach) i console.log(...)nie jest prawidłową nazwą klucza.
thefourtheye

@Cristy: Tak, myślę, że część powyższa dotycząca interpretacji (bit "Znaczenie") może nie być całkiem poprawna, ale części specyfikacji są, o ile wiem. Pasuje również błąd, który dostaję od V8: SyntaxError: Unexpected token ((wskazując na (w ()na końcu, a nie (w console.log(...)).
TJ Crowder,

@TJCrowder masz rację, skreślę to, ponieważ zmienia komunikat o błędzie, a to, co próbuję powiedzieć, nie jest przekazywane (tj. (Spowodowało, że tłumacz ustąpił po wyczerpujących próbach znalezienia poprawnej interpretacji i idzie „to musi być więc źle”), co i tak może być błędne, ponieważ nie wiem, jak naprawdę jest napisany tłumacz
Paul S.

Zastanawiam się, czy nie jest to prawidłowy token na tej pozycji, czy nie próbowałby wstawić średnika?
thefourtheye

2

Powodem, dla którego widzisz takie problemy, jest to, że sama konsola próbuje emulować globalny zakres kontekstu, na który obecnie celujesz. Próbuje również przechwycić wartości zwracane z instrukcji i wyrażeń, które piszesz w konsoli, tak aby były wyświetlane jako wyniki. Weź na przykład:

> 3 + 2
< 5

Tutaj wykonuje się tak, jakby to było wyrażenie, ale napisałeś to tak, jakby to była instrukcja. W normalnych skryptach wartość byłaby odrzucana, ale tutaj kod musi zostać zniekształcony wewnętrznie (jak zawijanie całej instrukcji kontekstem funkcji i returninstrukcją), co powoduje różnego rodzaju dziwne efekty, w tym problemy, których doświadczasz.

Jest to również jeden z powodów, dla których nagi kod ES6 w skryptach działa dobrze, ale nie działa w konsoli Chrome Dev Tools.

Spróbuj wykonać to w konsoli Node i Chrome:

{ let a = 3 }

W Node lub <script>tagu działa dobrze, ale w konsoli daje Uncaught SyntaxError: Unexpected identifier. Zapewnia również link do źródła, w postaci VMxxx:1którego możesz kliknąć, aby sprawdzić oceniane źródło, które jest wyświetlane jako:

({ let a = 3 })

Więc dlaczego to zrobił?

Odpowiedź brzmi: musi przekonwertować Twój kod na wyrażenie, aby wynik mógł zostać zwrócony do wywołującego i wyświetlony w konsoli. Możesz to zrobić, zawijając instrukcję w nawiasach, co czyni ją wyrażeniem, ale także powoduje, że powyższy blok jest niepoprawny składniowo (wyrażenie nie może mieć deklaracji bloku).

Konsola próbuje naprawić te skrajne przypadki, sprytnie podchodząc do kodu, ale myślę, że to wykracza poza zakres tej odpowiedzi. Możesz zgłosić błąd, aby sprawdzić, czy to jest coś, co rozważaliby naprawienie.

Oto dobry przykład czegoś bardzo podobnego:

https://stackoverflow.com/a/28431346/46588

Najbezpieczniejszym sposobem, aby kod działał, jest upewnienie się, że można go uruchomić jako wyrażenie i sprawdzenie SyntaxError łącza źródłowego, aby zobaczyć, jaki jest rzeczywisty kod wykonawczy, i przeprowadzić inżynierię wsteczną z tego rozwiązania. Zwykle oznacza parę strategicznie umieszczonych nawiasów.

W skrócie: konsola próbuje jak najdokładniej emulować globalny kontekst wykonania, ale ze względu na ograniczenia interakcji z silnikiem v8 i semantyką JavaScript jest to czasami trudne lub niemożliwe do rozwiązania.


1
O to chodzi, zależy mi na parametrze, ale nie działa z zestawem parametrów.
XCS

OK, rozumiem twój punkt widzenia. Różnica polega na sposobie, w jaki konsola narzędzi deweloperskich Chrome faktycznie wykonuje kod. Zmienię odpowiedź, aby to odzwierciedlić.
Klemen Slavič

0

Zadałem takie pytanie:

@getify Mam takie pytanie: aby utworzyć wzorzec #IIFE, używamy parans wokół deklaracji funkcji, aby przekształcić ją w wyrażenie funkcji, a następnie wywołać ją. Teraz w funkcji strzałek IIFE, po co nam parany ?! Czy funkcja strzałki nie jest już domyślnym wyrażeniem ?!

a to jest odpowiedź Kyle'a Simpsona:

funkcja strzałkowa jest wyrażeniem, ale potrzebujemy otaczających parens b / c "pierwszeństwa operatorów" (sorta), tak aby ostatnie pareny wywołujące strzałkę-IIFE miały zastosowanie do całej funkcji, a nie tylko do ostatniego elementu jej treści .

x => console.log(x)(4)

vs

(x => console.log(x))(4)

- getify (@getify) 12 czerwca 2020


Moje pytanie brzmiało, dlaczego działa na niektórych kompilatorach, a na innych nie.
XCS

Dzieje się tak, ponieważ różne kompilatory zachowują się inaczej w niektórych szczegółach, podobnie jak różne przeglądarki, które oczywiście mają różne kompilatory
Ershad Qaderi

Masz rację, zachowują się inaczej, ale specyfikacje JavaScript są takie same dla nich wszystkich. Byłem ciekawy, który z nich miał rację, co specyfikacja JS mówi o tym przypadku, a zwłaszcza jak to możliwe, że działa bez argumentów, ale nie z argumentami. Szukałem bardziej technicznej odpowiedzi.
XCS

Twój przykład jest dość oczywisty, w pierwszym przypadku powinien rzeczywiście zadzwonić console.log(x)(4).
XCS

Zgaduję, ale myślę, że bardzo rozsądne jest wyjaśnienie tego w ten sposób: w wyrażeniach funkcyjnych strzałek, gdy nie używamy parametru, musimy użyć parenów i to bardzo wyraźnie pokazuje silnikowi, że jest to strzałka wyrażenie funkcyjne, ale kiedy mamy pojedynczy parametr, pareny są dowolne, co może nie być zbyt jasne, że jest to funkcja i myli silnik, aby rozwiązać nieporozumienie, musimy umieścić parę parenów wokół całego wyrażenia funkcji
Ershad Qaderi
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.