Mengapa saya bisa menggunakan fungsi sebelum didefinisikan dalam JavaScript?

167

Kode ini selalu berfungsi, bahkan di berbagai browser:

function fooCheck() {
  alert(internalFoo()); // We are using internalFoo() here...

  return internalFoo(); // And here, even though it has not been defined...

  function internalFoo() { return true; } //...until here!
}

fooCheck();

Saya tidak dapat menemukan referensi tunggal mengapa itu harus bekerja. Saya pertama kali melihat ini dalam catatan presentasi John Resig, tetapi hanya disebutkan. Tidak ada penjelasan di sana atau di mana pun dalam hal ini.

Bisakah seseorang tolong beri saya pencerahan?

Edu Felipe
sumber
3
Di versi firefox yang lebih baru, ini tidak berfungsi jika kode berada dalam coba / tangkap. Lihat biola ini: jsfiddle.net/qzzc1evt
Joshua Smith

Jawaban:

217

The functiondeklarasi sihir dan menyebabkan identifier untuk terikat sebelum sesuatu dalam kode-blok yang * dijalankan.

Ini berbeda dari tugas dengan functionekspresi, yang dievaluasi dalam urutan top-down normal.

Jika Anda mengubah contoh untuk mengatakan:

var internalFoo = function() { return true; };

itu akan berhenti bekerja.

Deklarasi fungsi secara sintaksis cukup terpisah dari ekspresi fungsi, meskipun mereka terlihat hampir identik dan dapat ambigu dalam beberapa kasus.

Ini didokumentasikan dalam standar ECMAScript , bagian 10.1.3 . Sayangnya ECMA-262 bukan dokumen yang sangat mudah dibaca bahkan oleh standar-standar!

*: fungsi, blok, modul atau skrip yang berisi.

bobince
sumber
Saya kira itu benar-benar tidak dapat dibaca. Saya baru saja membaca bagian yang Anda tunjuk 10.1.3 dan tidak mengerti mengapa ketentuan di sana akan menyebabkan perilaku ini. Terima kasih untuk informasi.
Edu Felipe
2
@ karena Oke, saya mulai meragukan diri saya sendiri ketika saya tidak dapat menemukan satu pun istilah "mengangkat" di halaman ini. Semoga komentar ini memiliki Google Juice ™ yang cukup untuk memperbaikinya :)
Mathias Bynens
2
Ini adalah kombo pertanyaan / jawaban yang populer. Pertimbangkan memperbarui dengan tautan / kutipan ke spesifikasi beranotasi ES5. (Yang sedikit lebih mudah diakses.)
1
Artikel ini memiliki beberapa contoh: JavaScript-Scoping-and-Hoisting
Lombas
Saya menemukan beberapa perpustakaan menggunakan fungsi sebelum definisi, bahkan beberapa bahasa mengizinkannya secara resmi, ex. Haskell. Sejujurnya, ini mungkin bukan hal yang buruk, karena Anda dapat menulis sedikit lebih ekspresif dalam beberapa kasus.
windmaomao
27

Ini disebut HOISTING - Memanggil (memanggil) fungsi sebelum didefinisikan.

Dua jenis fungsi yang ingin saya tulis adalah:

Fungsi Ekspresi & Fungsi Deklarasi

  1. Fungsi ekspresi:

    Ekspresi fungsi dapat disimpan dalam variabel sehingga tidak perlu nama fungsi. Mereka juga akan dinamai sebagai fungsi anonim (fungsi tanpa nama).

    Untuk memanggil (memanggil) fungsi-fungsi ini, mereka selalu membutuhkan nama variabel . Fungsi semacam ini tidak akan berfungsi jika dipanggil sebelum didefinisikan yang berarti Pengangkatan tidak terjadi di sini. Kita harus selalu mendefinisikan fungsi ekspresi terlebih dahulu dan memohonnya.

    let lastName = function (family) {
     console.log("My last name is " + family);
    };
    let x = lastName("Lopez");

    Ini adalah bagaimana Anda dapat menulisnya dalam ECMAScript 6:

    lastName = (family) => console.log("My last name is " + family);
    
    x = lastName("Lopez");
  2. Fungsi Deklarasi:

    Fungsi yang dideklarasikan dengan sintaks berikut tidak dieksekusi segera. Mereka "disimpan untuk digunakan nanti" dan akan dieksekusi nanti, ketika mereka dipanggil (dipanggil). Jenis fungsi ini berfungsi jika Anda menyebutnya SEBELUM atau SETELAH di tempat yang telah ditentukan. Jika Anda memanggil fungsi deklarasi sebelum didefinisikan, mengangkat berfungsi dengan benar.

    function Name(name) {
      console.log("My cat's name is " + name);
    }
    Name("Chloe");

    Mengangkat contoh:

    Name("Chloe");
    function Name(name) {
       console.log("My cat's name is " + name);
    }
Negin
sumber
let fun = theFunction; fun(); function theFunction() {}juga akan berfungsi (Node dan browser)
fider
14

Peramban membaca HTML Anda dari awal hingga akhir dan dapat menjalankannya saat dibaca dan diuraikan menjadi potongan yang dapat dieksekusi (deklarasi variabel, definisi fungsi, dll.) Tetapi pada titik mana pun hanya dapat menggunakan apa yang telah didefinisikan dalam skrip sebelum titik itu.

Ini berbeda dari konteks pemrograman lain yang memproses (kompilasi) semua kode sumber Anda, mungkin menautkannya bersama-sama dengan perpustakaan yang Anda butuhkan untuk menyelesaikan referensi, dan membangun modul yang dapat dieksekusi, di mana titik eksekusi dimulai.

Kode Anda dapat merujuk ke objek bernama (variabel, fungsi lain, dll.) Yang didefinisikan lebih jauh, tetapi Anda tidak dapat menjalankan kode rujukan sampai semua bagian tersedia.

Ketika Anda terbiasa dengan JavaScript, Anda akan sangat menyadari kebutuhan Anda untuk menulis sesuatu dalam urutan yang tepat.

Revisi: Untuk mengonfirmasi jawaban yang diterima (di atas), gunakan Firebug untuk melangkah ke bagian skrip halaman web. Anda akan melihatnya melompat dari fungsi ke fungsi, hanya mengunjungi baris pertama, sebelum benar-benar mengeksekusi kode apa pun.

dkretz
sumber
3

Beberapa bahasa memiliki persyaratan bahwa pengidentifikasi harus didefinisikan sebelum digunakan. Alasan untuk ini adalah bahwa kompiler menggunakan pass tunggal pada kode sumber.

Tetapi jika ada beberapa pass (atau beberapa cek ditunda) Anda dapat hidup sempurna tanpa persyaratan itu. Dalam hal ini, kode mungkin pertama kali dibaca (dan ditafsirkan) dan kemudian tautannya diatur.

Toon Krijthe
sumber
2

Saya hanya menggunakan JavaScript sedikit. Saya tidak yakin apakah ini akan membantu, tetapi terlihat sangat mirip dengan apa yang Anda bicarakan dan mungkin memberikan wawasan:

http://www.dustindiaz.com/javascript-function-declaration-ambiguity/

RailsSon
sumber
Tautan tidak lagi mati.
mwclarke
1
Tautannya sudah mati.
Jerome Indefenzo
Ini snapshot dari archive.org. Sepertinya penulis menghapus seluruh situs web mereka karena memiliki konten yang ketinggalan zaman, bukan berarti posting blog ini termasuk dalam kategori itu.
jkmartindale
1

Badan fungsi "internalFoo" perlu pergi ke suatu tempat pada waktu parsing, jadi ketika kode dibaca (alias parsing) oleh JS interpreter, struktur data untuk fungsi tersebut dibuat dan namanya ditetapkan.

Baru kemudian, kemudian kode dijalankan, JavaScript benar-benar mencoba mencari tahu apakah "internalFoo" ada dan apa itu dan apakah itu bisa dipanggil, dll.

Aaron Digulla
sumber
-4

Untuk alasan yang sama, yang berikut ini akan selalu dimasukkan ke foodalam namespace global:

if (test condition) {
    var foo;
}
Andrew Hedges
sumber
8
Sebenarnya, itu untuk alasan yang sangat berbeda. The ifblok tidak membuat ruang lingkup, sementara function()blok selalu menciptakan satu. Alasan sebenarnya adalah bahwa definisi nama javascript global terjadi pada fase kompilasi, sehingga bahkan jika kode tidak berjalan, nama tersebut didefinisikan. (Maaf butuh waktu lama untuk berkomentar)
Edu Felipe