Berikut ini rundown pada formulir standar yang membuat fungsi: (Awalnya ditulis untuk pertanyaan lain, tetapi diadaptasi setelah dipindahkan ke pertanyaan kanonik.)
Ketentuan:
Daftar cepat:
Deklarasi Fungsi
"Anonim" function
Ekspresi (yang terlepas dari istilah tersebut, terkadang membuat fungsi dengan nama)
Bernama function
Ekspresi
Penginisialisasi Fungsi Accessor (ES5 +)
Ekspresi Fungsi Panah (ES2015 +) (yang, seperti ekspresi fungsi anonim, tidak melibatkan nama eksplisit, namun dapat membuat fungsi dengan nama)
Deklarasi Metode di Object Inisialisasi (ES2015 +)
Konstruktor dan Deklarasi Metode dalam class
(ES2015 +)
Deklarasi Fungsi
Bentuk pertama adalah deklarasi fungsi , yang terlihat seperti ini:
function x() {
console.log('x');
}
Deklarasi fungsi adalah deklarasi ; itu bukan pernyataan atau ungkapan. Dengan demikian, Anda tidak mengikutinya dengan ;
(meskipun melakukannya tidak berbahaya).
Deklarasi fungsi diproses ketika eksekusi memasuki konteks di mana ia muncul, sebelumnya kode langkah-demi-langkah dijalankan. Fungsi yang dibuatnya diberi nama yang tepat (x
dalam contoh di atas), dan nama itu dimasukkan ke dalam lingkup di mana deklarasi muncul.
Karena diproses sebelum kode langkah-demi-langkah dalam konteks yang sama, Anda dapat melakukan hal-hal seperti ini:
x(); // Works even though it's above the declaration
function x() {
console.log('x');
}
Sampai ES2015, spec tidak menutupi apa yang mesin JavaScript harus dilakukan jika Anda menempatkan sebuah deklarasi fungsi di dalam struktur pengendalian seperti try
, if
, switch
, while
, dll, seperti ini:
if (someCondition) {
function foo() { // <===== HERE THERE
} // <===== BE DRAGONS
}
Dan karena mereka sudah diproses sebelumnya kode langkah-demi-langkah dijalankan, sulit untuk mengetahui apa yang harus dilakukan ketika mereka berada dalam struktur kontrol.
Meskipun melakukan ini tidak ditentukan sampai ES2015, itu adalah ekstensi yang diijinkan untuk mendukung deklarasi fungsi dalam blok. Sayangnya (dan mau tidak mau), mesin yang berbeda melakukan hal yang berbeda.
Pada ES2015, spesifikasi mengatakan apa yang harus dilakukan. Sebenarnya, ini memberikan tiga hal terpisah untuk dilakukan:
- Jika dalam mode longgar bukan pada browser web, mesin JavaScript seharusnya melakukan satu hal
- Jika dalam mode longgar di browser web, mesin JavaScript seharusnya melakukan hal lain
- Jika dalam mode ketat (browser atau tidak), mesin JavaScript seharusnya melakukan hal lain lagi
Aturan untuk mode longgar rumit, tetapi dalam mode ketat , deklarasi fungsi dalam blok mudah: Mereka lokal ke blok (mereka memiliki ruang lingkup blok , yang juga baru di ES2015), dan mereka diangkat ke atas dari blok. Begitu:
"use strict";
if (someCondition) {
foo(); // Works just fine
function foo() {
}
}
console.log(typeof foo); // "undefined" (`foo` is not in scope here
// because it's not in the same block)
function
Ekspresi "Anonim"
Bentuk umum kedua disebut ekspresi fungsi anonim :
var y = function () {
console.log('y');
};
Seperti semua ekspresi, itu dievaluasi ketika dicapai dalam pelaksanaan kode langkah demi langkah.
Di ES5, fungsi yang dibuat ini tidak memiliki nama (anonim). Dalam ES2015, fungsi tersebut diberi nama jika mungkin dengan menyimpulkannya dari konteks. Pada contoh di atas, namanya akan y
. Hal serupa dilakukan ketika fungsinya adalah nilai dari initializer properti. (Untuk perincian tentang kapan ini terjadi dan aturannya, cari SetFunctionName
dalam spesifikasi - itu muncul di semua tempat.)
function
Ekspresi Bernama
Bentuk ketiga adalah ekspresi fungsi bernama ("NFE"):
var z = function w() {
console.log('zw')
};
Fungsi yang dibuat ini memiliki nama yang tepat ( w
dalam hal ini). Seperti semua ekspresi, ini dievaluasi ketika tercapai dalam langkah-demi-langkah pelaksanaan kode. Nama fungsi tidak ditambahkan ke ruang lingkup di mana ekspresi muncul; nama berada dalam lingkup fungsi itu sendiri:
var z = function w() {
console.log(typeof w); // "function"
};
console.log(typeof w); // "undefined"
Perhatikan bahwa NFE sering menjadi sumber bug untuk implementasi JavaScript. IE8 dan sebelumnya, misalnya, menangani NFE sepenuhnya salah , menciptakan dua fungsi yang berbeda pada dua waktu yang berbeda. Versi awal Safari juga memiliki masalah. Berita baiknya adalah bahwa versi browser saat ini (IE9 dan yang lebih baru, Safari saat ini) tidak lagi memiliki masalah itu. (Tetapi pada saat penulisan ini, sayangnya, IE8 masih digunakan secara luas, dan menggunakan NFE dengan kode untuk web secara umum masih bermasalah.)
Penginisialisasi Fungsi Accessor (ES5 +)
Terkadang fungsi dapat menyelinap masuk tanpa disadari; itulah yang terjadi dengan fungsi accessor . 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"
Perhatikan bahwa ketika saya menggunakan fungsi, saya tidak menggunakan ()
! Itu karena ini merupakan fungsi pengakses untuk sebuah properti. Kami mendapatkan dan mengatur properti dengan cara biasa, tetapi di balik layar, fungsinya disebut.
Anda juga dapat membuat accessor fungsi dengan Object.defineProperty
, Object.defineProperties
, dan argumen kedua kurang dikenal untuk Object.create
.
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 n => n * 2
benda itu bersembunyi di map()
telepon? Itu sebuah fungsi.
Beberapa hal tentang fungsi panah:
Mereka tidak punya sendiri this
. Sebaliknya, mereka dekat selama ini this
dari konteks di mana mereka didefinisikan. (Mereka juga menutup arguments
dan, jika relevan super
,.) Ini berarti bahwa di this
dalam mereka sama dengan di this
mana mereka diciptakan, dan tidak dapat diubah.
Seperti yang Anda perhatikan di atas, Anda tidak menggunakan kata kunci function
; sebaliknya, Anda gunakan =>
.
The n => n * 2
contoh di atas merupakan salah satu bentuk dari mereka. Jika Anda memiliki beberapa argumen untuk melewati 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 yang Array#map
melewati entri sebagai argumen pertama, dan indeks sebagai argumen kedua.)
Dalam kedua kasus, tubuh fungsi hanyalah sebuah ekspresi; nilai pengembalian fungsi akan secara otomatis menjadi hasil dari ekspresi itu (Anda tidak menggunakan eksplisit return
).
Jika Anda melakukan lebih dari sekedar satu ekspresi, gunakan {}
dan eksplisit return
(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 ringkas . (Juga: Fungsi panah ringkas .) Yang dengan { ... }
mendefinisikan tubuh adalah fungsi panah dengan tubuh fungsi . (Juga: Fungsi panah verbose .)
Deklarasi Metode di Object Inisialisasi (ES2015 +)
ES2015 memungkinkan bentuk pendek menyatakan properti yang referensi fungsi yang disebut definisi metode ; terlihat seperti ini:
var o = {
foo() {
}
};
yang hampir setara di ES5 dan sebelumnya adalah:
var o = {
foo: function foo() {
}
};
perbedaannya (selain verbositas) adalah suatu metode dapat digunakan super
, tetapi suatu fungsi tidak bisa. Jadi misalnya, jika Anda memiliki objek yang didefinisikan (katakanlah) valueOf
menggunakan sintaksis metode, itu bisa digunakan super.valueOf()
untuk mendapatkan nilai Object.prototype.valueOf
akan kembali (sebelum mungkin melakukan sesuatu yang lain dengan itu), sedangkan versi ES5 harus melakukan Object.prototype.valueOf.call(this)
sebaliknya.
Itu juga berarti bahwa metode memiliki referensi ke objek yang didefinisikan, jadi jika objek itu bersifat sementara (misalnya, Anda meneruskannya menjadi Object.assign
salah satu objek sumber), sintaksis metode dapat berarti bahwa objek tersebut dipertahankan dalam memori ketika sebaliknya bisa saja sampah dikumpulkan (jika mesin JavaScript tidak mendeteksi situasi itu dan menanganinya jika tidak ada metode yang digunakan super
).
Konstruktor dan Deklarasi Metode dalam class
(ES2015 +)
ES2015 membawa kita class
sintaks, termasuk konstruktor dan metode yang dideklarasikan:
class Person {
constructor(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
getFullName() {
return this.firstName + " " + this.lastName;
}
}
Ada dua deklarasi fungsi di atas: Satu untuk konstruktor, yang mendapatkan nama Person
, dan satu untuk getFullName
, yang merupakan fungsi yang ditugaskan untuk Person.prototype
.