ต่อไปนี้เป็นบทสรุปในรูปแบบมาตรฐานที่สร้างฟังก์ชั่น: (แต่เดิมเขียนสำหรับคำถามอื่น แต่ดัดแปลงหลังจากถูกย้ายไปยังคำถามมาตรฐาน)
ข้อตกลง:
รายการด่วน:
ประกาศฟังก์ชั่น
การfunction
แสดงออก"ไม่ระบุชื่อ" (ซึ่งแม้จะมีคำว่าบางครั้งสร้างฟังก์ชั่นที่มีชื่อ)
function
นิพจน์ที่ระบุชื่อ
Accessor Function Initializer (ES5 +)
Arrow Function Expression (ES2015 +) (ซึ่งเช่นนิพจน์ฟังก์ชันนิรนามไม่เกี่ยวข้องกับชื่อที่ชัดเจนและยังสามารถสร้างฟังก์ชั่นที่มีชื่อ)
การประกาศวิธีการใน Object Initializer (ES2015 +)
ตัวสร้างและการประกาศวิธีในclass
(ES2015 +)
ประกาศฟังก์ชั่น
แบบฟอร์มแรกคือการประกาศฟังก์ชั่นซึ่งมีลักษณะดังนี้:
function x() {
console.log('x');
}
การประกาศฟังก์ชั่นคือการประกาศ ; มันไม่ใช่คำสั่งหรือการแสดงออก ดังนั้นคุณจะไม่ปฏิบัติตามด้วย;
(แม้ว่าการทำเช่นนั้นจะไม่เป็นอันตราย)
การประกาศฟังก์ชั่นจะถูกประมวลผลเมื่อการดำเนินการเข้าสู่บริบทที่ปรากฏก่อนที่จะมีการดำเนินการรหัสทีละขั้นตอนใด ๆ ฟังก์ชั่นที่สร้างขึ้นจะได้รับชื่อที่ถูกต้อง ( x
ในตัวอย่างด้านบน) และชื่อนั้นจะอยู่ในขอบเขตที่การประกาศปรากฏขึ้น
เนื่องจากประมวลผลก่อนโค้ดทีละขั้นตอนในบริบทเดียวกันคุณสามารถทำสิ่งนี้:
x(); // Works even though it's above the declaration
function x() {
console.log('x');
}
จนกระทั่ง ES2015 ข้อมูลจำเพาะไม่ครอบคลุมสิ่งที่เป็นเครื่องมือ JavaScript ที่ควรจะทำอย่างไรถ้าคุณใส่ฟังก์ชั่นการประกาศภายในโครงสร้างการควบคุมเช่นtry
, if
, switch
, while
ฯลฯ เช่นนี้
if (someCondition) {
function foo() { // <===== HERE THERE
} // <===== BE DRAGONS
}
และเนื่องจากมันถูกประมวลผลก่อนที่จะมีการเรียกใช้รหัสทีละขั้นตอนจึงเป็นการยากที่จะทราบว่าต้องทำอย่างไรเมื่ออยู่ในโครงสร้างการควบคุม
แม้ว่าการทำเช่นนี้จะไม่ได้ระบุไว้จนกระทั่ง ES2015 แต่เป็นส่วนขยายที่อนุญาตเพื่อรองรับการประกาศฟังก์ชันในบล็อก น่าเสียดาย (และหลีกเลี่ยงไม่ได้) เครื่องยนต์ต่าง ๆ ทำสิ่งที่แตกต่างกัน
ในฐานะของ ES2015 สเปคบอกว่าจะทำอย่างไร ในความเป็นจริงมันมีสามสิ่งที่ต้องทำ:
- หากอยู่ในโหมดหลวมไม่ได้อยู่ในเว็บเบราว์เซอร์เอ็นจิ้น JavaScript จะต้องทำสิ่งหนึ่ง
- หากอยู่ในโหมดหลวมบนเว็บเบราว์เซอร์เอ็นจิ้น JavaScript จะต้องทำอย่างอื่น
- หากอยู่ในโหมดเข้มงวด (เบราว์เซอร์หรือไม่) เอ็นจิ้น JavaScript จะต้องทำสิ่งอื่น
กฎสำหรับโหมดหลวมนั้นยุ่งยาก แต่ในโหมดเข้มงวดการประกาศฟังก์ชันในบล็อกนั้นง่าย: พวกมันอยู่ในบล็อก (มีขอบเขตบล็อกซึ่งยังใหม่ใน ES2015) และถูกยกขึ้นไปด้านบน ของบล็อก ดังนั้น:
"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
นิพจน์"ไม่ระบุชื่อ"
รูปแบบทั่วไปที่สองเรียกว่านิพจน์ฟังก์ชันที่ไม่ระบุชื่อ :
var y = function () {
console.log('y');
};
เช่นเดียวกับนิพจน์ทั้งหมดการประเมินจะถูกประเมินเมื่อมีการเข้าถึงในการเรียกใช้โค้ดทีละขั้นตอน
ใน ES5 ฟังก์ชั่นนี้สร้างไม่มีชื่อ (มันไม่ระบุชื่อ) ใน ES2015 ฟังก์ชันจะถูกกำหนดชื่อถ้าเป็นไปได้โดยอนุมานจากบริบท y
ในตัวอย่างข้างต้นชื่อจะเป็น สิ่งที่คล้ายกันเกิดขึ้นเมื่อฟังก์ชันคือค่าของ initializer คุณสมบัติ (สำหรับรายละเอียดเกี่ยวกับสิ่งที่เกิดขึ้นและกฎค้นหาSetFunctionName
ในสเปค - มันจะปรากฏขึ้นทั่วสถานที่)
function
นิพจน์ที่ระบุชื่อ
รูปแบบที่สามคือนิพจน์ฟังก์ชันที่ระบุชื่อ ("NFE"):
var z = function w() {
console.log('zw')
};
ฟังก์ชันนี้สร้างขึ้นมีชื่อที่ถูกต้อง ( w
ในกรณีนี้) เช่นเดียวกับนิพจน์ทั้งหมดการประเมินนี้จะถูกประเมินเมื่อถึงในการเรียกใช้โค้ดทีละขั้นตอน ชื่อของฟังก์ชั่นจะไม่ถูกเพิ่มลงในขอบเขตที่การแสดงออกจะปรากฏขึ้น; ชื่ออยู่ในขอบเขตภายในฟังก์ชันเอง:
var z = function w() {
console.log(typeof w); // "function"
};
console.log(typeof w); // "undefined"
โปรดทราบว่า NFE มักเป็นแหล่งของข้อบกพร่องสำหรับการใช้งาน JavaScript ยกตัวอย่างเช่น IE8 และรุ่นก่อนหน้าจัดการกับ NFE ไม่ถูกต้องอย่างสมบูรณ์สร้างฟังก์ชันที่แตกต่างกันสองรายการในเวลาที่ต่างกันสองครั้ง Safari เวอร์ชันก่อนหน้ามีปัญหาเช่นกัน ข่าวดีก็คือเบราว์เซอร์เวอร์ชันปัจจุบัน (IE9 ขึ้นไปซาฟารีปัจจุบัน) ไม่มีปัญหาเหล่านั้นอีกแล้ว (แต่จากการเขียนนี้น่าเศร้าที่ IE8 ยังคงใช้กันอย่างแพร่หลายและดังนั้นการใช้ NFEs ที่มีรหัสสำหรับเว็บโดยทั่วไปยังคงเป็นปัญหาอยู่)
Accessor Function Initializer (ES5 +)
บางครั้งฟังก์ชั่นสามารถแอบในส่วนใหญ่ไม่มีใครสังเกตเห็น; เป็นกรณีที่มีฟังก์ชั่นการเข้าถึง นี่คือตัวอย่าง:
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"
โปรดทราบว่าเมื่อฉันใช้ฟังก์ชั่นฉันไม่ได้ใช้()
! นั่นเป็นเพราะมันเป็นฟังก์ชั่นการเข้าถึงสำหรับคุณสมบัติ เราได้รับและตั้งค่าคุณสมบัติในแบบปกติ แต่เบื้องหลังเรียกว่าฟังก์ชั่น
นอกจากนี้คุณยังสามารถสร้างฟังก์ชั่นการเข้าถึงด้วยObject.defineProperty
, และไม่ค่อยมีคนรู้จักอาร์กิวเมนต์ที่สองไปObject.defineProperties
Object.create
ลูกศรฟังก์ชั่นการแสดงออก (ES2015 +)
ES2015 นำเราฟังก์ชั่นลูกศร นี่คือตัวอย่างหนึ่ง:
var a = [1, 2, 3];
var b = a.map(n => n * 2);
console.log(b.join(", ")); // 2, 4, 6
เห็นn => n * 2
สิ่งที่ซ่อนตัวอยู่ในmap()
สาย? นั่นคือฟังก์ชั่น
สองสามสิ่งเกี่ยวกับฟังก์ชั่นลูกศร:
this
พวกเขาไม่ได้เป็นของตัวเอง แต่พวกเขาใกล้กว่าthis
ของบริบทที่พวกเขากำลังที่กำหนดไว้ (พวกเขายังปิดarguments
และที่เกี่ยวข้อง, super
.) ซึ่งหมายความว่าthis
ภายในพวกเขาเหมือนกันกับthis
ที่พวกเขากำลังสร้างและไม่สามารถเปลี่ยนแปลงได้
ดังที่คุณได้สังเกตเห็นด้วยข้างต้นคุณไม่ได้ใช้คำหลักfunction
; คุณใช้=>
แทน
n => n * 2
ตัวอย่างข้างต้นเป็นรูปแบบหนึ่งของพวกเขา หากคุณมีหลายอาร์กิวเมนต์ที่จะผ่านฟังก์ชั่นคุณใช้ parens:
var a = [1, 2, 3];
var b = a.map((n, i) => n * i);
console.log(b.join(", ")); // 0, 2, 6
(โปรดจำไว้ว่าArray#map
ส่งผ่านรายการเป็นอาร์กิวเมนต์แรกและดัชนีเป็นรายการที่สอง)
ในทั้งสองกรณีร่างกายของฟังก์ชันเป็นเพียงการแสดงออก ค่าส่งคืนของฟังก์ชันจะเป็นผลลัพธ์ของนิพจน์นั้นโดยอัตโนมัติ (คุณไม่ได้ใช้อย่างชัดเจนreturn
)
หากคุณทำมากกว่านิพจน์เดียวให้ใช้{}
และชัดเจนreturn
(ถ้าคุณต้องการส่งคืนค่า) ตามปกติ:
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));
รุ่นโดยไม่ต้อง{ ... }
เรียกว่าฟังก์ชั่นลูกศรที่มีร่างกายแสดงออกหรือร่างกายกระชับ (นอกจากนี้: การกระชับ . ฟังก์ชั่นลูกศร) เป็นผู้หนึ่งที่มี{ ... }
การกำหนดร่างกายเป็นฟังก์ชั่นที่มีลูกศรร่างกายทำงาน (เช่น: ฟังก์ชั่นลูกศรverbose )
การประกาศวิธีการใน Object Initializer (ES2015 +)
ES2015 ช่วยให้รูปแบบที่สั้นลงประกาศทรัพย์สินที่อ้างอิงฟังก์ชั่นที่เรียกว่าเป็นคำนิยามวิธี ; ดูเหมือนว่านี้:
var o = {
foo() {
}
};
เกือบเทียบเท่าใน ES5 และรุ่นก่อนหน้าจะเป็น:
var o = {
foo: function foo() {
}
};
ความแตกต่าง (นอกเหนือจากคำฟุ่มเฟื่อย) คือวิธีการที่สามารถใช้super
แต่ฟังก์ชั่นไม่สามารถ ตัวอย่างเช่นหากคุณมีวัตถุที่กำหนด (พูด) valueOf
โดยใช้ไวยากรณ์วิธีการก็สามารถใช้super.valueOf()
เพื่อรับค่าObject.prototype.valueOf
จะได้กลับมา (ก่อนที่จะทำอย่างอื่นกับมัน) ในขณะที่รุ่น ES5 จะต้องทำObject.prototype.valueOf.call(this)
แทน
นั่นก็หมายความว่าวิธีนี้มีการอ้างอิงไปยังวัตถุที่มันถูกกำหนดไว้ดังนั้นหากวัตถุนั้นเป็นแบบชั่วคราว (เช่นคุณกำลังส่งมันไปObject.assign
เป็นวัตถุหนึ่งในแหล่งที่มา) ไวยากรณ์ของวิธีการอาจหมายถึงวัตถุนั้นจะถูกเก็บไว้ ในหน่วยความจำเมื่อไม่เช่นนั้นอาจมีการรวบรวมขยะ (หากเอนจิน JavaScript ไม่ตรวจจับสถานการณ์นั้นและจัดการหากไม่มีวิธีใดใช้super
)
ตัวสร้างและการประกาศวิธีในclass
(ES2015 +)
ES2015 ทำให้เรามีclass
ไวยากรณ์รวมถึงตัวสร้างและวิธีการประกาศ:
class Person {
constructor(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
getFullName() {
return this.firstName + " " + this.lastName;
}
}
มีฟังก์ชั่นที่สองการประกาศดังกล่าวเป็นหนึ่งสำหรับการสร้างซึ่งได้รับชื่อPerson
และสำหรับซึ่งเป็นฟังก์ชั่นได้รับมอบหมายให้getFullName
Person.prototype