Mengapa nama fungsi JavaScript saya bentrok?

97

Saya menulis skrip berikut hanya untuk melihat apa yang terjadi ketika variabel dan fungsi yang memiliki fungsi yang ditugaskan padanya memiliki nama yang bentrok:

var f = function() {
    console.log("Me original.");
}

function f() {
    console.log("Me duplicate.");
}

f();

Output yang saya dapatkan adalah "Saya asli." Mengapa fungsi lainnya tidak dipanggil?

Juga, jika saya mengubah tugas awal saya menjadi var f = new function() {, saya mendapatkan "Saya asli", diikuti dengan pepatah TypeError object is not a function. Bisakah seseorang menjelaskan?

ankush981
sumber
26
@ Dean.DePue - Tidak ada kebingungan di bagian JavaScript. Aturan untuk menanganinya cukup jelas (dan dijelaskan oleh Benjamin dalam jawabannya).
Quentin
4
Curiosity, masih merupakan cara terbaik untuk belajar tentang suatu bahasa. :-D
Cerbrus
2
Selain itu, saya membayangkan sangat tidak mungkin untuk sesuatu yang tidak material seperti "JavaScript" untuk "merasa" bingung (atau emosi apa pun, dalam hal ini) ;-)
Cerbrus
2
Mengapa hoisting harus membalik urutan pada contoh kedua?
Cerbrus
5
Langkah-langkah untuk mengembangkan pengetahuan tentang javascript: 1) Gunakan 'gunakan ketat' 2) Selalu gunakan jslint atau jshint 3) Cari hal-hal yang dikeluhkan oleh jslint atau jshint 4) Bilas dan ulangi
steve-er-rino

Jawaban:

170

Deklarasi fungsi diangkat (dipindahkan ke atas) di JavaScript. Meskipun salah dalam hal urutan penguraian, kode yang Anda miliki secara semantik sama dengan yang berikut karena deklarasi fungsi diangkat:

function f() {
    console.log("Me duplicate.");
}
var f = function() {
    console.log("Me original.");
}


f();

Yang pada gilirannya, dengan pengecualian nama fungsinya sama dengan:

var f = function() {
    console.log("Me duplicate.");
}
var f = function() {
    console.log("Me original.");
}


f();

Yang pada gilirannya, karena pengangkat variabel adalah sama dengan:

var f;
f = function() {
    console.log("Me duplicate.");
}
f = function() {
    console.log("Me original.");
}

f();

Yang menjelaskan apa yang Anda dapatkan, Anda mengganti fungsinya. Secara lebih umum, beberapa vardeklarasi diperbolehkan dalam JavaScript - var x = 3; var x = 5sangat legal. Dalam standar ECMAScript 6 baru, letpernyataan melarang ini.

Artikel oleh @kangax ini melakukan pekerjaan yang luar biasa dalam mendemistifikasi fungsi di javascript

Benjamin Gruenbaum
sumber
2
Dapatkah Anda benar-benar menyederhanakan function f()untuk var f = function()yang banyak? Apakah nama hoisting dan fungsi benar-benar satu - satunya perbedaan?
djechlin
6
@ djechlin dalam konteks pertanyaan ini - ya. Secara umum, ini lebih halus - lihat stackoverflow.com/questions/336859/… . Dari perspektif kompilator, mereka berbeda - tetapi dari perspektif pemrogram - kami cukup dekat untuk mengklaimnya. Itulah mengapa saya menambahkan bahwa "meskipun salah dalam hal urutan penguraian, kode yang Anda miliki secara semantik sama dengan" alih-alih mengatakan "adalah sama dengan". Poin yang bagus.
Benjamin Gruenbaum
1
@dotslash tolong jangan mengedit pertanyaan asli Anda dan mengubahnya, yang dianggap berperilaku buruk di sini - juga, mencampurkan beberapa pertanyaan dalam satu juga dianggap berperilaku buruk di sini. Sebagai gantinya, Anda dapat mengajukan pertanyaan baru atau, jika menurut Anda itu terlalu kecil, mintalah klarifikasi di komentar (memang untuk itulah mereka). Dalam kode di atas, kedua versi fdiangkat, dan "Me Original"versi baru saja diangkat kemudian , masing-masing dipindahkan ke atas tetapi dalam urutan yang sama. Saya hanya ingin menambahkan bahwa secara umum, Anda tidak boleh memberi nama beberapa fungsi dengan cara yang sama :)
Benjamin Gruenbaum
5
Dalam mode ketat, Anda tidak dapat varmenggunakan nama yang sama dua kali dalam lingkup yang sama.
Hoffmann
4
"Itu seharusnya jelas" - bagi Anda mungkin, tetapi tidak jelas bagi saya pada satu titik, dan tidak jelas bagi OP ketika dia menanyakannya, dan penamaan, dan lebih umum lagi bagaimana lingkungan leksikal dikelola dalam JavaScript adalah satu dari hal tersulit untuk dipahami saat pertama kali mempelajari JavaScript bagi saya. Saya tidak akan begitu cepat menghina orang yang tidak memahaminya.
Benjamin Gruenbaum
10

Jika sepertinya tidak ada yang menjawab pertanyaan tindak lanjut Anda, maka saya akan menjawabnya di sini, meskipun Anda biasanya harus mengajukan pertanyaan lanjutan sebagai pertanyaan terpisah.

Anda bertanya mengapa ini:

var f = new function() {
    console.log("Me original.");
}

function f() {
    console.log("Me duplicate.");
}

f();

mencetak "Saya asli." dan kemudian terjadi kesalahan.

Apa yang terjadi di sini adalah newpenyebab fungsi digunakan sebagai konstruktor. Jadi ini setara dengan berikut ini:

function myConstructor() {
    console.log("Me original.");
}
var f = new myConstructor();

function f() {
    console.log("Me duplicate.");
}

f();

Dan berkat fungsi hoisting yang dijelaskan Benjamin, hal di atas pada dasarnya setara dengan ini:

var myConstructor = function() {
    console.log("Me original.");
};
var f = function() {
    console.log("Me duplicate.");
};

f = new myConstructor();

f();

Ekspresi ini:

var f = new function() {
    console.log("Me original.");
}

menyebabkan objek baru dibangun dan ditetapkan f, menggunakan fungsi anonim sebagai konstruktor. "Saya asli." dicetak saat konstruktor dijalankan. Tapi objek yang dibangun itu sendiri bukanlah sebuah fungsi, jadi ketika ini akhirnya dijalankan:

f();

Anda mendapatkan kesalahan, karena fbukan fungsi.

JLRishe
sumber
Oh, luar biasa! Terima kasih banyak telah bersusah payah menjawabnya! :) :)
ankush981
2

Maafkan saya jika ini adalah cara yang salah untuk mendekati menambahkan poin. Saya belum sering berada di sini, dan akan menerima arahan dan / atau kritik yang membangun.

Jawaban Benjamin menjawab pertanyaan OP dengan sangat baik, tetapi saya ingin menambahkan satu penyesuaian yang akan memberi kita tur lengkap tentang pengangkatan dan keanehannya.

Jika kita memulai kode asli dengan panggilan ke f, seperti:

f();

var f = function() {
   console.log("Me original.");
};

function f() {
   console.log("Me duplicate.");
}

f();

Outputnya kemudian akan menjadi:

Me duplicate.
Me original.

Alasannya karena vardan functionpernyataan dikibarkan dengan cara yang sedikit berbeda.

Untuk varitu deklarasi dipindahkan ke bagian atas ruang lingkup saat ini *, tetapi setiap tugas tidak mengangkat. Sejauh nilai dari variabel yang dideklarasikan berjalan, itu tidak ditentukan sampai baris tugas asli tercapai.

Untuk functionpernyataan , baik deklarasi maupun definisi diangkat. Ekspresi fungsi , seperti yang digunakan dalam var f = function() {...konstruksi, tidak diangkat.

Jadi setelah mengangkat, eksekusi seolah-olah kodenya adalah:

var f; // declares var f, but does not assign it.

// name and define function f, shadowing the variable
function f() { 
  console.log("Me duplicate.");
}

// call the currently defined function f
f(); 

// assigns the result of a function expression to the var f,
// which shadows the hoisted function definition once past this point lexically
f = function() { 
  console.log("Me original."); 
}

// calls the function referenced by the var f
f();

* Semua cakupan JavaScript adalah leksikal, atau fungsi, cakupan, tetapi sepertinya itu hanya akan membingungkan jika menggunakan kata f pada saat itu.

codelahoma
sumber