Var functionName = function () {} vs function functionName () {}

Baru-baru ini, saya mulai mendukung kode JavaScript pengguna lain. Saya memperbaiki kesalahan, menambah fungsi, dan juga mencoba merapikan kode dan membuatnya lebih konsisten.

Pengembang sebelumnya menggunakan dua cara untuk mendeklarasikan fungsi, dan saya tidak tahu apakah ada alasan untuk ini atau tidak.

Dua cara adalah:

 var functionOne = function() { // Some code }; 
 function functionTwo() { // Some code } 

Apa alasan untuk menggunakan dua metode yang berbeda ini dan apa pro dan kontra dari masing-masing metode tersebut? Adakah sesuatu yang bisa dilakukan dengan satu metode yang tidak bisa dilakukan dengan yang lain?

6296
03 дек. Richard Garside bertanya pada 3 Des 2008-12-03 14:31 '08 pukul 14:31 2008-12-03 14:31
@ 37 jawaban
  • 1
  • 2

Perbedaannya adalah bahwa functionOne adalah ekspresi dari suatu fungsi dan oleh karena itu ia ditentukan hanya ketika garis ini tercapai, sedangkan functionTwo adalah deklarasi fungsi dan ditentukan segera setelah fungsi atau skrip di sekitarnya dieksekusi (karena pengangkatan ).

Misalnya, fungsi ekspresi:

4669
03 дек. balasan yang diberikan oleh Greg 03 des. 2008-12-03 14:37 '08 pada 14:37 2008-12-03 14:37

Pertama saya ingin memperbaiki Greg: function abc(){} juga terbatas; - nama abc didefinisikan di area di mana definisi ini ditemukan. Contoh:

 function xyz(){ function abc(){}; // abc is defined here... } // ...but not here 

Kedua, Anda dapat menggabungkan kedua gaya:

 var xyz = function abc(){}; 

xyz akan didefinisikan seperti biasa, abc - tidak terdefinisi di semua browser, tetapi Internet Explorer - tidak bergantung pada definisinya. Tetapi dia akan didefinisikan di dalam tubuhnya:

 var xyz = function abc(){ // xyz is visible here // abc is visible here } // xyz is visible here // abc is undefined here 

Jika Anda ingin menggunakan nama samaran di semua browser, gunakan jenis iklan ini:

 function abc(){}; var xyz = abc; 

Dalam hal ini, baik xyz dan abc adalah alias dari objek yang sama:

 console.log(xyz === abc); // prints "true" 

Salah satu alasan kuat untuk menggunakan gaya gabungan adalah atribut "nama" untuk objek fungsi ( tidak didukung oleh Internet Explorer ). Pada dasarnya, ketika Anda mendefinisikan fungsi seperti

 function abc(){}; console.log(abc.name); // prints "abc" 

namanya ditetapkan secara otomatis. Tetapi ketika Anda mendefinisikannya sebagai

 var abc = function(){}; console.log(abc.name); // prints "" 

namanya kosong - kami telah membuat fungsi anonim dan menugaskannya beberapa variabel.

Alasan bagus lainnya untuk menggunakan gaya gabungan adalah menggunakan nama internal pendek untuk merujuknya, memberikan nama panjang yang tidak bertentangan untuk pengguna eksternal:

 // Assume really.long.external.scoped is {} really.long.external.scoped.name = function shortcut(n){ // Let it call itself recursively: shortcut(n - 1); // ... // Let it pass itself as a callback: someFunction(shortcut); // ... } 

Pada contoh di atas, kita dapat melakukan hal yang sama dengan nama eksternal, tetapi itu akan terlalu rumit (dan lebih lambat).

(Cara lain untuk mengatasi diri Anda adalah dengan menggunakan arguments.callee , yang masih relatif panjang dan tidak didukung dalam mode ketat.)

Turun, JavaScript menangani kedua pernyataan secara berbeda. Ini adalah deklarasi fungsi:

 function abc(){} 

abc sini didefinisikan di mana-mana di area saat ini:

 // We can call it here abc(); // Works // Yet, it is defined down there. function abc(){} // We can call it again abc(); // Works 

Selain itu, ia bangkit menggunakan return :

 // We can call it here abc(); // Works return; function abc(){} 

Ini adalah ekspresi fungsi:

 var xyz = function(){}; 

xyz didefinisikan di sini dari tujuan:

 // We can't call it here xyz(); // UNDEFINED!!! // Now it is defined xyz = function(){} // We can call it here xyz(); // works 

Deklarasi fungsi dan ekspresi fungsi adalah alasan sebenarnya bahwa ada perbedaan, ditunjukkan oleh Greg.

Fakta menyenangkan:

 var xyz = function abc(){}; console.log(xyz.name); // Prints "abc" 

Secara pribadi, saya lebih suka deklarasi "ekspresi fungsi", karena dengan cara ini saya dapat mengontrol visibilitas. Ketika saya mendefinisikan fungsi tipe

 var abc = function(){}; 

Saya tahu bahwa saya mendefinisikan fungsi secara lokal. Ketika saya mendefinisikan fungsi tipe

 abc = function(){}; 

Saya tahu bahwa saya mendefinisikannya secara global, menunjukkan bahwa saya tidak mendefinisikan abc di rantai wilayah. Gaya definisi ini stabil bahkan ketika digunakan di dalam eval() . Meski definisi

 function abc(){}; 

tergantung pada konteks dan dapat membuat Anda bertanya-tanya di mana itu didefinisikan, terutama dalam kasus eval() - Jawaban: Tergantung pada browser.

1846
03 дек. Jawabannya diberikan oleh Eugene Lazutkin 03 Desember. 2008-12-03 20:43 '08 pada 8:43 malam 2008-12-03 20:43

Berikut ini adalah ringkasan dari formulir standar yang membuat fungsi: (Awalnya ditulis untuk pertanyaan lain, tetapi diadaptasi setelah transisi ke pertanyaan kanonik.)

Ketentuan:

Daftar cepat:

  • Deklarasi fungsi

  • Ekspresi function "Anonim" (yang, meskipun ada istilahnya, terkadang membuat fungsi dengan nama)

  • Ekspresi function dinamai

  • Initializer Fungsi Akses (ES5 +)

  • Ekspresi fungsi panah (ES2015 +) (yang, seperti ekspresi fungsi anonim, tidak mengandung nama eksplisit dan dapat membuat fungsi dengan nama)

  • Deklarasi metode dalam penginisialisasi objek (ES2015 +)

  • Deklarasi konstruktor dan metode di class (ES2015 +)

Deklarasi fungsi

Bentuk pertama adalah deklarasi fungsi yang terlihat seperti ini:

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

Deklarasi fungsi adalah iklan; itu bukan pernyataan atau ungkapan. Jadi Anda tidak mengikutinya ; (meskipun tidak berbahaya).

Deklarasi fungsi diproses ketika eksekusi memasuki konteks yang muncul sebelum mengeksekusi kode >x dalam contoh di atas), dan nama ini ditempatkan di area di mana deklarasi muncul.

Karena diproses sebelum kode >

 x(); // Works even though it above the declaration function x() { console.log('x'); } 

Sebelum ES2015, spesifikasi tidak mencakup apa yang harus dilakukan mesin JavaScript jika Anda menempatkan deklarasi fungsi di dalam struktur kontrol, misalnya, try , if , switch , while , dll. Misalnya:

 if (someCondition) { function foo() { // <===== HERE THERE } // <===== BE DRAGONS } 

Dan karena mereka diproses sebelum menjalankan kode >

Meskipun itu tidak ditunjukkan sebelum ES2015, itu adalah ekstensi yang valid untuk mendukung deklarasi fungsi dalam blok. Sayangnya (dan mau tidak mau), mesin yang berbeda melakukan hal yang berbeda.

Dimulai dengan ES2015, spesifikasi mengatakan apa yang harus dilakukan. Bahkan, ia memberikan tiga tindakan terpisah:

  1. Jika dalam mode bebas tidak ada di browser web, mesin JavaScript harus melakukan satu hal.
  2. Jika dalam mode bebas di browser web, mesin JavaScript harus melakukan sesuatu yang lain.
  3. Jika dalam mode ketat (browser atau tidak), mesin JavaScript harus melakukan satu hal lagi.

Aturan untuk mode bebas rumit, tetapi dalam mode ketat, deklarasi fungsi dalam blok sederhana: mereka lokal untuk blok (mereka memiliki ruang lingkup blok, yang juga baru di ES2015), dan mereka naik ke blok. Jadi:

 "use strict"; if (someCondition) { foo(); // Works just fine function foo() { } } console.log(typeof foo); // "undefined" ('foo' is not in scope here // because it not in the same block) 

Ekspresi function "anonim"

Bentuk umum kedua disebut ekspresi fungsi anonim:

 var y = function () { console.log('y'); }; 

Seperti semua ekspresi, dihitung saat mencapai eksekusi kode >

Dalam ES5, fungsi yang dibuat tidak memiliki nama (itu anonim). Dalam ES2015, suatu fungsi diberi nama kapanpun memungkinkan, membawanya keluar dari konteks. Pada contoh di atas, nama akan y . Sesuatu seperti ini terjadi ketika fungsi adalah nilai dari initializer properti. (Untuk informasi lebih lanjut tentang kapan ini terjadi dan tentang aturan, temukan SetFunctionName dalam spesifikasi - ini muncul di mana-mana.)

Ekspresi function dinamai

Bentuk ketiga adalah ekspresi dengan fungsi bernama ("NFE"):

 var z = function w() { console.log('zw') }; 

Fungsi yang dibuatnya memiliki namanya sendiri (dalam hal ini, w ). Seperti semua ekspresi, ini dievaluasi ketika dicapai dengan eksekusi kode >

 var z = function w() { console.log(typeof w); // "function" }; console.log(typeof w); // "undefined" 

Harap perhatikan bahwa NFE sering menjadi sumber kesalahan untuk implementasi JavaScript. Misalnya, IE8 dan versi sebelumnya menangani NFE sepenuhnya salah , menciptakan dua fungsi berbeda pada dua titik waktu yang berbeda. Versi awal Safari juga memiliki masalah. Kabar baiknya adalah bahwa dalam versi browser saat ini (IE9 dan lebih tinggi, Safari saat ini), masalah seperti itu tidak ada lagi. (Namun sayangnya, pada saat penulisan ini, IE8 masih banyak digunakan, dan karena itu menggunakan NFE dengan kode untuk Internet secara keseluruhan masih bermasalah.)

Initializer Fungsi Akses (ES5 +)

Terkadang fungsi dapat menembus sebagian besar tanpa disadari; bagaimana dengan fungsi akses. Berikut ini sebuah contoh:

 var obj = { value: 0, get f() { return this.value; }, set f(v) { this.value = v; } }; console.log(obj.f); // 0 console.log(typeof obj.f); // "number" 

Harap dicatat bahwa ketika saya menggunakan fungsi, saya tidak menggunakan () ! Ini karena ini merupakan fungsi akses untuk properti. Kami mendapatkan dan mengatur properti dengan cara biasa, tetapi di balik layar sebuah fungsi dipanggil.

Anda juga dapat membuat fungsi akses menggunakan Object.defineProperty , Object.defineProperties dan argumen kedua Object.create kurang terkenal.

Ekspresi fungsi panah (ES2015 +)

ES2015 memberi kita fungsi panah. Ini salah satu contohnya:

 var a = [1, 2, 3]; var b = a.map(n => n * 2); console.log(b.join(", ")); // 2, 4, 6 

Lihat apa yang n => n * 2 disembunyikan di panggilan map() ? Ini sebuah fungsi.

Beberapa hal tentang fungsi panah:

  1. Mereka tidak punya this sendiri. Sebagai gantinya, mereka menutup konteks this di mana mereka didefinisikan. (Mereka juga dekat dengan arguments dan, jika sesuai, super .) Ini berarti bahwa this di dalamnya sama seperti this , di mana mereka diciptakan, dan tidak dapat diubah.

  2. Seperti yang Anda catat di atas, Anda tidak menggunakan function kata kunci; sebagai gantinya, Anda menggunakan => .

Contoh n => n * 2 atas adalah salah satu bentuknya. Jika Anda memiliki beberapa argumen untuk melewati suatu fungsi, Anda menggunakan parens:

 var a = [1, 2, 3]; var b = a.map((n, i) => n * i); console.log(b.join(", ")); // 0, 2, 6 

(Ingat, Array#map melewati catatan sebagai argumen pertama, dan indeks sebagai yang kedua.)

Dalam kedua kasus, fungsi tubuh hanyalah sebuah ekspresi; nilai pengembalian fungsi akan secara otomatis menjadi hasil dari ungkapan ini (Anda tidak menggunakan pengembalian eksplisit).

Jika Anda melakukan lebih dari satu ekspresi, gunakan {} dan pengembalian eksplisit (jika Anda perlu mengembalikan nilai), seperti biasa:

 var a = [ {first: "Joe", last: "Bloggs"}, {first: "Albert", last: "Bloggs"}, {first: "Mary", last: "Albright"} ]; a = a.sort((a, b) => { var rv = a.last.localeCompare(b.last); if (rv === 0) { rv = a.first.localeCompare(b.first); } return rv; }); console.log(JSON.stringify(a)); 

Versi tanpa {... } disebut fungsi panah dengan tubuh ekspresi atau tubuh pendek. (Juga: Fungsi singkat panah.) Fungsi dengan {... } mendefinisikan tubuh adalah fungsi panah dengan tubuh fungsi. (Juga: fungsi panah kata kerja.)

Deklarasi metode dalam penginisialisasi objek (ES2015 +)

ES2015 memungkinkan untuk formulir pernyataan properti yang lebih pendek yang mengacu pada fungsi yang disebut definisi metode; terlihat seperti ini:

 var o = { foo() { } }; 

hampir setara dengan ES5 dan versi sebelumnya:

 var o = { foo: function foo() { } }; 

Perbedaannya (kecuali verbositas) adalah bahwa metode tersebut dapat menggunakan super , tetapi fungsinya tidak bisa. Jadi, misalnya, jika Anda memiliki objek yang mendefinisikan (katakan) valueOf menggunakan sintaks metode, itu bisa menggunakan super.valueOf() untuk mendapatkan nilai Object.prototype.valueOf yang harus dikembalikan (sebelum seharusnya melakukan sesuatu sesuatu yang lain dengan itu), sedangkan Versi ES5 harus sebaliknya Object.prototype.valueOf.call(this) membuat Object.prototype.valueOf.call(this) .

Ini juga berarti bahwa metode memiliki referensi ke objek yang didefinisikan, oleh karena itu, jika objek ini bersifat sementara (misalnya, Anda meneruskannya ke Object.assign sebagai salah satu objek asli), sintaksis metode ini dapat berarti bahwa objek tersebut disimpan dalam memori, ketika sebaliknya dapat dikumpulkan oleh pengumpul sampah (jika mesin JavaScript tidak mendeteksi situasi ini dan tidak menanganinya, jika tidak ada metode yang menggunakan super ).

Deklarasi konstruktor dan metode di class (ES2015 +)

ES2015 memberi kami sintaks class , termasuk konstruktor dan metode yang dideklarasikan:

 class Person { constructor(firstName, lastName) { this.firstName = firstName; this.lastName = lastName; } getFullName() { return this.firstName + " " + this.lastName; } } 

Di atas adalah dua deklarasi fungsi: satu untuk konstruktor, yang bernama Person , dan getFullName untuk getFullName , yang merupakan fungsi yang ditugaskan ke Person.prototype .

574
04 марта '14 в 16:35 2014-03-04 16:35 Jawaban diberikan oleh TJ Crowder 04 Maret '14 di 16:35 2014-03-04 16:35

Berbicara tentang konteks global, pernyataan var dan FunctionDeclaration pada akhirnya membuat properti yang tidak dapat dihapus untuk objek global, tetapi nilai keduanya dapat ditimpa.

Perbedaan halus antara dua cara adalah bahwa ketika proses Instansiasi Variabel dimulai (sebelum eksekusi kode aktual), semua pengidentifikasi yang dideklarasikan dengan var akan diinisialisasi dengan undefined , dan yang digunakan oleh FunctionDeclaration akan tersedia mulai sekarang, misalnya:

  alert(typeof foo); // 'function', it already available alert(typeof bar); // 'undefined' function foo () {} var bar = function () {}; alert(typeof bar); // 'function' 

Penugasan pada FunctionExpression bar dilakukan sebelum eksekusi.

Properti global yang dibuat oleh FunctionDeclaration dapat ditimpa tanpa masalah dengan cara yang sama dengan nilai variabel, misalnya:

  function test () {} test = null; 

Perbedaan jelas lainnya antara dua contoh Anda adalah bahwa fungsi pertama tidak memiliki nama, tetapi yang kedua memilikinya, yang dapat sangat berguna saat debugging (misalnya, memeriksa tumpukan panggilan).

Tentang contoh pertama yang diedit ( foo = function() { alert('hello!'); }; ), Ini adalah tugas yang tidak dideklarasikan, saya sangat menyarankan Anda selalu menggunakan kata kunci var .

Ketika ditugaskan tanpa operator var , jika pengidentifikasi referensi tidak ditemukan dalam rantai lingkup, itu akan menjadi properti yang dapat dilepas dari objek global.

Selain itu, tugas yang tidak didaftarkan melempar ReferenceError pada ECMAScript 5 dalam Mode Ketat .

A harus membaca:

Catatan : Jawaban ini digabungkan dari pertanyaan lain , di mana keraguan dan kesalahpahaman utama dari OP adalah bahwa pengidentifikasi yang dideklarasikan dengan FunctionDeclaration tidak dapat ditimpa, yang tidak demikian halnya.

137
08 авг. jawabannya diberikan oleh CMS 08 Agustus. 2010-08-08 22:32 '10 pada 10:32 2010-08-08 22:32

Dua cuplikan kode yang Anda letakkan di sana akan berperilaku sama untuk hampir semua tujuan.

Namun, perbedaan dalam perilaku adalah bahwa dengan opsi pertama ( var functionOne = function() {} ) fungsi ini dapat dipanggil hanya setelah titik ini dalam kode.

Dalam varian kedua ( function functionTwo() ), fungsi tersedia untuk kode yang dieksekusi di atas, di mana fungsi tersebut dideklarasikan.

Ini disebabkan oleh fakta bahwa pada varian pertama fungsi tersebut ditugaskan ke variabel foo pada saat run time. Dalam fungsi kedua, pengenal ini ditugaskan untuk foo selama parsing.

Informasi teknis tambahan

JavaScript memiliki tiga cara untuk mendefinisikan fungsi.

  • Di cuplikan pertama Anda, ekspresi fungsi ditampilkan. Hal ini disebabkan oleh penggunaan operator "fungsi" untuk membuat fungsi - hasil dari operator ini dapat disimpan dalam variabel atau objek apa pun. Ekspresi fungsi sangat kuat. Ekspresi fungsi sering disebut "fungsi anonim" karena seharusnya tidak memiliki nama,
  • Contoh kedua adalah deklarasi fungsi. . Untuk membuat fungsi, gunakan operator "fungsi". Fungsi ini disediakan selama parsing dan dapat dipanggil di mana saja di area ini. Anda bisa menyimpannya nanti di variabel atau objek.
  • Cara ketiga untuk mendefinisikan suatu fungsi adalah konstruktor "Function ()" , yang tidak ditampilkan dalam pesan aslinya. Tidak disarankan untuk menggunakan ini, karena ia bekerja dengan cara yang sama seperti eval() , yang memiliki masalah sendiri.
115
20 апр. Jawaban diberikan oleh thomasrutter 20 Apr 2010-04-20 07:54 '10 pada 7:54 2010-04-20 07:54

Greg menjawab penjelasan terbaik

 functionTwo(); function functionTwo() { } 

Kenapa tidak ada bug? Kami selalu diajari bahwa ekspresi dieksekusi dari atas ke bawah (??)

Karena:

Deklarasi fungsi dan deklarasi variabel selalu dipindahkan ( hoisted ) tanpa terlihat ke atas wilayah mereka menggunakan penerjemah JavaScript. Parameter fungsional dan nama bahasa, jelas, sudah ada. ben cherry

Ini berarti kode tersebut:

 functionOne(); --------------- var functionOne; | is actually | functionOne(); var functionOne = function(){ | interpreted |--> }; | like | functionOne = function(){ --------------- }; 

Harap dicatat bahwa bagian dari penugasan deklarasi belum dimunculkan. Hanya namanya yang dinaikkan.

Tetapi dalam hal deklarasi fungsi, tubuh seluruh fungsi juga akan dinaikkan:

 functionTwo(); --------------- function functionTwo() { | is actually | }; function functionTwo() { | interpreted |--> } | like | functionTwo(); --------------- 
96
09 авг. jawabannya diberikan simple_human 09 Agustus. 2014-08-09 05:45 '14 jam 5:45 2014-08-09 05:45

Komentator lain telah mempertimbangkan perbedaan semantik antara dua opsi yang tercantum di atas. Saya ingin menunjukkan perbedaan gaya: hanya variasi "tujuan" yang dapat menetapkan properti objek lain.

Saya sering membuat modul JavaScript dengan pola ini:

 (function(){ var exports = {}; function privateUtil() { ... } exports.publicUtil = function() { ... }; return exports; })(); 

Menggunakan templat ini, fungsi publik Anda akan menggunakan tujuan, sementara fungsi pribadi Anda akan menggunakan iklan.

(Perhatikan juga bahwa tugas harus mengandung tanda titik koma setelah instruksi, sementara pengumuman melarangnya.)

87
03 марта '11 в 22:19 2011-03-03 22:19 membalas Sean McMillan pada 03 Maret '11 di 22:19 2011-03-03 22:19

Ilustrasi kapan lebih baik menggunakan metode pertama untuk yang kedua adalah ketika Anda harus menghindari mengesampingkan fungsi definisi sebelumnya.

Dengan

 if (condition){ function myfunction(){ // Some code } } 

definisi myfunction menimpa definisi sebelumnya, karena akan dieksekusi selama parsing.

Sementara

 if (condition){ var myfunction = function (){ // Some code } } 

melakukan pekerjaan yang benar untuk mendefinisikan myfunction hanya ketika condition dijalankan.

73
29 марта '13 в 16:26 2013-03-29 16:26 Jawaban diberikan oleh Mbengue Assane pada 29 Maret '13 pada 16:26 2013-03-29 16:26

Alasan penting adalah penambahan satu dan hanya satu variabel sebagai "root" dari namespace Anda ...

 var MyNamespace = {} MyNamespace.foo= function() { } 

atau

 var MyNamespace = { foo: function() { }, ... } 

Ada banyak metode untuk namespace. Ini menjadi lebih penting dengan banyaknya modul JavaScript yang tersedia.

Также см. Как объявить пространство имен в JavaScript?

59
ответ дан Rob 08 авг. '10 в 22:44 2010-08-08 22:44

Hoisting - это действие интерпретаторов JavaScript для перемещения всех объявлений переменных и функций в начало текущей объем.