ES6 segera memanggil fungsi panah

149

Mengapa ini bekerja di Node.jskonsol (diuji pada 4.1.1 dan 5.3.0) tetapi tidak berfungsi di browser (diuji di Chrome)? Blok kode ini harus membuat dan menjalankan fungsi anonim yang mencatat Ok.

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

Selain itu, sementara di atas berfungsi di Node, ini tidak berfungsi:

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

Atau ini:

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

Apa yang aneh adalah bahwa ketika parameter ditambahkan itu benar-benar melempar SyntaxErrorpada bagian yang segera dipanggil.

XCS
sumber
8
Pertanyaan bagus. Kedua versi parameter bekerja dengan Babel
CodingIntrigue
2
Karena minat, apakah (n => { console.log("Ok"); })();berhasil?
CodingIntrigue
Ya (n => { console.log("Ok"); })()berfungsi bahkan di konsol dev Chrome
XCS
dan, 3 tahun kemudian, jawabannya adalah? pasti salah satu dari 3 jawaban di bawah ini harus diterima ?!
joedotnot
@ joedotnot Saya tidak mendapatkan jawaban yang jelas, kebanyakan itu adalah implementasi aneh di Node.js. Sepertinya di versi terbaru versi Node.jspertama sudah tidak berfungsi lagi.
XCS

Jawaban:

194

Anda harus menjadikannya ekspresi fungsi alih-alih definisi fungsi yang tidak memerlukan nama dan menjadikannya JavaScript yang valid.

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

Apakah setara dengan IIFE

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

Dan alasan yang memungkinkan mengapa ini bekerja di Node.js tetapi tidak di chrome adalah karena parsernya mengartikannya sebagai fungsi yang mengeksekusi sendiri, karena ini

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

berfungsi dengan baik di Node.jsIni adalah ekspresi fungsi dan chrome dan firefox dan sebagian besar browser mengartikannya dengan cara ini. Anda harus menjalankannya secara manual.

Cara yang paling banyak diterima untuk memberitahu parser untuk mengharapkan ekspresi fungsi adalah dengan membungkusnya dalam parens, karena dalam JavaScript, parens tidak dapat berisi pernyataan. Pada titik ini, ketika parser menemukan kata kunci fungsi, ia tahu menguraikannya sebagai ekspresi fungsi dan bukan deklarasi fungsi.

Mengenai versi parametrized , ini akan berhasil.

((n) => {
  console.log('Ok');
})()
kosong
sumber
4
Contoh pertama berfungsi Node.jsdan benar-benar mencatat nilainya. Pertanyaan saya adalah mengapa cara kerjanya? Dan mengapa tidak ketika saya menambahkan parameter?
XCS
1
Saya cukup akrab dengan IIFEs dan tahu cara memperbaiki kode saya. Saya hanya ingin tahu mengapa, misalnya, saya IIFEtidak berfungsi ketika nparameter ditambahkan, meskipun berfungsi tanpa parameter.
XCS
3
Saya tidak mengundurkan diri, tetapi pertanyaannya adalah mengapa versi parameter tidak bekerja di Node ketika definisi yang sama persis tanpa parameter - itu tidak menanyakan perbedaan antara implementasi Node / Chrome dari fungsi anonim
CodingIntrigue
1
Itu baik untuk diketahui tetapi tidak menjawab pertanyaan, seperti yang disebutkan sebelumnya, - mengapa versi parameter tidak bekerja di Node ketika definisi yang sama persis tanpa parameter
jkris
Tapi apa yang setara function(){}()dengan fungsi panah? Jika saya ingin fungsi objek terkena, ekspresi fungsi melindungi terhadap itu.
dabadaba
18

Tak satu pun dari ini akan bekerja tanpa tanda kurung.

Mengapa?

Karena sesuai dengan spesifikasi:

  1. ArrowFunction yang terdaftar di bawah AssignmentExpression
  2. The LHS dari CallExpression harus MemberExpression , SuperCall atau CallExpression

Jadi ArrowFunction tidak bisa berada di LHS dari CallExpression .


Apa artinya ini secara efektif dalam bagaimana =>harus ditafsirkan, adalah bahwa ia bekerja pada tingkat yang sama dengan operator penugasan =, +=dll.

Berarti

  • x => {foo}() tidak menjadi(x => {foo})()
  • Penerjemah mencoba menafsirkannya sebagai x => ({foo}())
  • Jadi itu masih sebuah SyntaxError
  • Jadi penerjemah memutuskan bahwa (pasti salah dan melemparkan SyntaxError

Ada bug pada Babel tentang hal itu di sini juga.

Paul S.
sumber
Itu adalah beberapa poin yang valid, tetapi jika saya mencoba mengganti versi pertama yang berfungsi, dengan: () => ({console.log('Ok')}())tidak lagi berfungsi. Jadi itu tidak benar-benar menafsirkannya seperti itu.
XCS
@ Christy Ini bukan produksi Fungsi Panah yang valid. Ia berpikir bahwa Anda mencoba untuk membuat Objek dengan Objek literal (terlampir oleh tanda kurung) dan console.log(...)bukan nama kunci yang valid.
theouroureye
@ Christy: Ya, saya pikir bagian interpretasi di atas ("Arti" bit) mungkin tidak cukup benar, tetapi bagian spesifikasi sejauh yang saya tahu. Ini juga cocok dengan kesalahan yang saya dapatkan dari V8: SyntaxError: Unexpected token ((menunjuk ke (dalam ()di akhir, bukan (di console.log(...)).
TJ Crowder
@TJCrowder Anda benar, saya akan mencoretnya karena itu mengubah pesan kesalahan dan apa yang saya coba katakan tidak disampaikan (mis. (Menyebabkan penerjemah menyerah setelah upaya yang melelahkan untuk menemukan interpretasi yang valid dan melanjutkan "Ini pasti salah kalau begitu"), yang mungkin juga salah karena saya tidak tahu bagaimana penerjemah itu benar-benar ditulis
Paul S.
Saya bertanya-tanya apakah itu bukan token yang valid pada posisi ini, bukankah itu akan mencoba untuk memasukkan titik koma?
theouroureye
2

Alasan Anda melihat masalah seperti ini adalah konsol itu sendiri mencoba meniru lingkup global konteks yang saat ini Anda targetkan. Itu juga mencoba untuk menangkap nilai kembali dari pernyataan dan ekspresi yang Anda tulis di konsol, sehingga muncul sebagai hasil. Ambil, misalnya:

> 3 + 2
< 5

Di sini, dieksekusi seolah-olah itu ekspresi, tetapi Anda telah menulis seolah-olah itu adalah pernyataan. Dalam skrip normal, nilainya akan dibuang, tetapi di sini, kodenya harus secara internal hancur (seperti membungkus seluruh pernyataan dengan konteks fungsi dan returnpernyataan), yang menyebabkan segala macam efek aneh, termasuk masalah yang Anda alami.

Ini juga salah satu alasan mengapa beberapa kode ES6 telanjang dalam skrip berfungsi dengan baik tetapi tidak di konsol Alat Dev Chrome.

Coba jalankan ini di Node dan konsol Chrome:

{ let a = 3 }

Di Node atau <script>tag berfungsi dengan baik, tetapi di konsol, itu memberi Uncaught SyntaxError: Unexpected identifier. Ini juga memberi Anda tautan ke sumber dalam bentuk VMxxx:1yang dapat Anda klik untuk memeriksa sumber yang dievaluasi, yang muncul sebagai:

({ let a = 3 })

Jadi mengapa ia melakukan ini?

Jawabannya adalah perlu mengubah kode Anda menjadi ekspresi sehingga hasilnya dapat dikembalikan ke pemanggil dan ditampilkan di konsol. Anda dapat melakukan ini dengan membungkus pernyataan dalam tanda kurung, yang membuatnya menjadi ekspresi, tetapi juga membuat blok di atas secara sintaksis salah (ekspresi tidak dapat memiliki deklarasi blok).

Konsol mencoba memperbaiki kasus tepi ini dengan menjadi pandai tentang kode, tapi itu di luar cakupan jawaban ini, saya pikir. Anda dapat mengajukan bug untuk melihat apakah itu sesuatu yang mereka pertimbangkan untuk memperbaikinya.

Berikut ini contoh bagus dari sesuatu yang sangat mirip:

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

Cara paling aman untuk membuat kode Anda berfungsi adalah memastikan kode itu dapat dijalankan sebagai ekspresi dan memeriksa SyntaxErrortautan sumber untuk melihat apa kode eksekusi yang sebenarnya dan merekayasa balik solusi dari itu. Biasanya itu berarti sepasang kurung yang ditempatkan secara strategis.

Singkatnya: konsol mencoba untuk meniru konteks eksekusi global seakurat mungkin, tetapi karena keterbatasan interaksi dengan mesin v8 dan semantik JavaScript, terkadang ini sulit atau tidak mungkin untuk dipecahkan.

Klemen Slavič
sumber
1
Itulah intinya, saya peduli tentang parameter, tetapi tidak bekerja dengan set parameter.
XCS
Oke, saya mengerti maksud Anda. Perbedaannya terletak pada cara konsol Alat Dev Chrome menjalankan kode Anda. Saya akan mengedit jawaban untuk mencerminkan ini.
Klemen Slavič
0

Saya mengajukan pertanyaan seperti ini:

@getify Saya punya pertanyaan ini: untuk menghasilkan pola #IIFE kita menggunakan parans di sekitar deklarasi fungsi untuk mengubahnya menjadi ekspresi fungsi dan memohonnya. Sekarang dalam fungsi panah IIFE, mengapa kita perlu paran ?! Bukankah fungsi panah sudah merupakan ekspresi secara default ?!

dan ini adalah jawaban Kyle Simpson:

fungsi panah adalah expr, tetapi kita membutuhkan parens sekitarnya b / c dari "operator precedence" (agak), sehingga parens akhir untuk memanggil panah-IIFE berlaku untuk seluruh fungsi dan bukan hanya token terakhir dari tubuhnya .

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

vs.

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

- getify (@getify) 12 Juni 2020

Irsyad Qaderi
sumber
Pertanyaan saya adalah mengapa ini bekerja pada beberapa kompiler dan tidak pada yang lain.
XCS
Itu karena kompiler yang berbeda berperilaku berbeda dalam beberapa detail, sama seperti browser yang berbeda, yang tentu saja memiliki kompiler yang berbeda
Ershad Qaderi
Anda benar, mereka berperilaku berbeda, tetapi spesifikasi JavaScript sama untuk mereka semua. Saya ingin tahu yang mana yang benar, apa yang dikatakan JS spec tentang kasus ini dan terutama bagaimana bisa bekerja tanpa argumen tetapi tidak dengan argumen. Saya mencari respons yang lebih teknis.
XCS
Contoh Anda cukup jelas, dalam kasus pertama memang harus menelepon console.log(x)(4).
XCS
Saya hanya menebak-nebak di sini tetapi saya pikir sangat masuk akal untuk menjelaskannya seperti ini: dalam ekspresi fungsi panah ketika kita tidak menggunakan parameter kita harus menggunakan parens dan itu membuatnya sangat jelas untuk mesin bahwa ini adalah panah ekspresi fungsi, tetapi ketika kita memiliki parameter tunggal parens arbitrer yang mungkin tidak terlalu jelas bahwa ini fungsi dan membingungkan mesin itu, untuk menyelesaikan kebingungan kita harus meletakkan sepasang paren di sekitar seluruh ekspresi fungsi
Irsyad Qaderi