คำนำ
ใน eCmascript มีสองวิธีที่ใช้กันมากที่สุดสองวิธีในการสร้างวัตถุฟังก์ชั่นคือการใช้การแสดงออกของฟังก์ชั่นหรือใช้การประกาศฟังก์ชั่น ในเรื่องนี้ข้อกำหนด ECMASCript ทำให้ชัดเจนว่าการประกาศฟังก์ชั่นจะต้องมีตัวระบุเสมอซึ่งเป็นสิ่งที่เราเรียกว่าชื่อฟังก์ชั่นและการแสดงออกของฟังก์ชั่นสามารถละเว้นได้ มาดูการแนะนำรายละเอียดเกี่ยวกับความแตกต่างระหว่างทั้งสอง
การประกาศฟังก์ชั่นคืออะไร?
การประกาศฟังก์ชั่นสามารถกำหนดตัวแปรฟังก์ชันที่มีชื่อโดยไม่ต้องกำหนดค่าให้กับตัวแปร การประกาศฟังก์ชั่นเป็นโครงสร้างอิสระที่ไม่สามารถซ้อนกันในโมดูลที่ไม่ใช้งานได้ มันสามารถเปรียบเทียบกับการประกาศตัวแปร เช่นเดียวกับการประกาศตัวแปรจะต้องเริ่มต้นด้วย "var" การประกาศฟังก์ชั่นจะต้องเริ่มต้นด้วย "ฟังก์ชั่น"
ตัวอย่างเช่น
Function Bar () {return 3;}ECMA 5 (13.0) คำจำกัดความไวยากรณ์:
function Identifier ( FormalParameterList[opt] ) { FunctionBody }
ชื่อฟังก์ชั่นมีอยู่ในขอบเขตและขอบเขตหลักของตัวเอง (มิฉะนั้นฟังก์ชั่นจะไม่พร้อมใช้งาน)
function bar () {return 3;} bar () // 3bar // functionฟังก์ชั่นนิพจน์คืออะไร?
ฟังก์ชั่นนิพจน์กำหนดฟังก์ชันเป็นส่วนหนึ่งของคำสั่งนิพจน์ (โดยปกติแล้วการกำหนดตัวแปร) ฟังก์ชั่นที่กำหนดโดยนิพจน์ฟังก์ชั่นสามารถตั้งชื่อหรือไม่ระบุชื่อ การแสดงออกของฟังก์ชั่นไม่สามารถเริ่มต้นด้วย "ฟังก์ชั่น" (ตัวอย่างการโทรด้วยตนเองต่อไปนี้ควรปิดล้อมในวงเล็บ)
ตัวอย่างเช่น
// ฟังก์ชันนิรนาม ExpressionVar a = function () {return 3;} // ชื่อฟังก์ชัน ExpressionVar A = function bar () {return 3;} // การแสดงออกของฟังก์ชั่นการเรียกใช้งานด้วยตนเอง (ฟังก์ชั่น sayshello () {แจ้งเตือน ("สวัสดี!");ECMA 5 (13.0) คำจำกัดความไวยากรณ์:
function Identifieropt ( FormalParameterList[opt] ) { FunctionBody }
(คำจำกัดความนี้รู้สึกไม่สมบูรณ์เพราะไม่สนใจเงื่อนไข: คำสั่งอุปกรณ์ต่อพ่วงเป็นนิพจน์และไม่ได้เริ่มต้นด้วย "ฟังก์ชั่น")
ชื่อฟังก์ชั่น (ถ้ามี) ไม่มีอยู่ด้านนอกขอบเขต (เปรียบเทียบกับการประกาศฟังก์ชั่น)
คำสั่งฟังก์ชั่นคืออะไร?
คำสั่งฟังก์ชั่นเป็นอีกวิธีหนึ่งของการประกาศฟังก์ชั่น แต่ Kangax ชี้ให้เห็นว่าใน Mozilla คำสั่งฟังก์ชั่นเป็นส่วนขยายของการประกาศฟังก์ชันทำให้สามารถใช้คำแถลงการประกาศฟังก์ชั่นได้ทุกที่ที่อนุญาตให้ใช้คำสั่ง อย่างไรก็ตามคำสั่งฟังก์ชั่นไม่ใช่มาตรฐานในขณะนี้ดังนั้นจึงไม่แนะนำให้ใช้ในการพัฒนาผลิตภัณฑ์
เริ่มต้นด้วยการทดสอบเล็ก ๆ น้อย ๆ เดาว่าจะเกิดอะไรขึ้นในสถานการณ์ต่อไปนี้?
คำถามที่ 1:
function foo () {function bar () {return 3; } return bar (); แถบฟังก์ชัน () {return 8; }} Alert (foo ());คำถามที่ 2:
function foo () {var bar = function () {return 3; - Return Bar (); var bar = function () {return 8; };} Alert (foo ());คำถามที่ 3:
การแจ้งเตือน (foo ()); function foo () {var bar = function () {return 3; - Return Bar (); var bar = function () {return 8; -คำถามที่ 4:
function foo () {return bar (); var bar = function () {return 3; - var bar = function () {return 8; };} Alert (foo ());หากคำตอบของคุณไม่ใช่ 8, 3, 3 และ [ประเภทข้อผิดพลาด: แถบไม่ใช่ฟังก์ชั่น] จากนั้นอ่านต่อ ... (แม้ว่าคุณจะตอบถูกต้องคุณต้องอ่านต่อ)
ทีนี้มาอธิบายการทดสอบก่อนหน้า
คำถามที่ 1 ใช้การประกาศฟังก์ชั่นซึ่งหมายความว่าพวกเขาได้รับการยก ...
เดี๋ยวก่อนยกอะไร?
นี่คือใบเสนอราคาจาก Ben Cherry: "การประกาศฟังก์ชั่นและตัวแปรฟังก์ชั่นมักจะถูกย้าย ('hoisted') ไปที่ด้านบนของขอบเขตปัจจุบันโดยล่าม JavaScript"
เมื่อการประกาศฟังก์ชั่นได้รับการส่งเสริมร่างกายฟังก์ชั่นทั้งหมดจะได้รับการส่งเสริมตามนั้นดังนั้นรหัสของคำถามที่ 1 จะดำเนินการเช่นนี้หลังจากถูกอธิบายโดยล่าม:
// ** ลำดับการประมวลผลแบบจำลองสำหรับคำถาม 1 ** ฟังก์ชั่น foo () {// define bar เมื่อแถบฟังก์ชัน () {return 3; } // redefine it function bar () {return 8; } // ส่งคืนแถบ Return Return (); // 8} Alert (foo ());อย่างไรก็ตามเรามักจะบอกว่ารหัสที่อยู่เบื้องหลังคำสั่ง Return ไม่สามารถเรียกใช้ ...
ในระหว่างการดำเนินการของ JavaScript มีสองแนวคิด: บริบท (ECMA 5 แบ่งมันออกเป็นคำศัพท์สภาพแวดล้อมตัวแปรและสิ่งนี้ binding) และกระบวนการ (ชุดคำสั่งที่เรียกว่าตามลำดับ) เมื่อโปรแกรมเข้าสู่โดเมนการดำเนินการการประกาศจะทำให้เกิดสภาพแวดล้อมที่แปรปรวน พวกเขาแตกต่างจากคำสั่ง (เช่นผลตอบแทน) และไม่ปฏิบัติตามกฎการใช้คำสั่ง
ฟังก์ชั่นนิพจน์จะได้รับการส่งเสริมหรือไม่?
มันขึ้นอยู่กับการแสดงออก ตัวอย่างเช่นนิพจน์แรกในคำถามที่ 2:
var bar = function () {return 3;};(แถบ var) ทางด้านซ้ายของเครื่องหมายเท่ากันคือการประกาศตัวแปร การประกาศตัวแปรจะได้รับการส่งเสริม แต่การแสดงออกที่ได้รับมอบหมายจะไม่ ดังนั้นเมื่อบาร์ได้รับการเลื่อนตำแหน่งล่ามจะเริ่มต้นเช่นนี้: var bar = ไม่ได้กำหนด คำจำกัดความของฟังก์ชั่นจะไม่ได้รับการส่งเสริม
(ตัวแปร ECMA 5 12.2 ที่มีค่าเริ่มต้นถูกกำหนดโดยการกำหนดการแสดงออกเมื่อมีการเรียกใช้งาน VariableStatement ไม่ใช่เมื่อตัวแปรถูกสร้างขึ้น)
ดังนั้นรหัสของคำถาม 2 จะเรียกใช้ในลำดับต่อไปนี้:
// ** ลำดับการประมวลผลแบบจำลองสำหรับคำถามที่ 2 ** ฟังก์ชั่น foo () {// การประกาศสำหรับแต่ละฟังก์ชันนิพจน์ var bar = ไม่ได้กำหนด; var bar = ไม่ได้กำหนด; // นิพจน์ฟังก์ชั่นแรกคือการดำเนินการ bar = function () {return 3; - // ฟังก์ชั่นที่สร้างขึ้นโดยนิพจน์ฟังก์ชันแรกถูกเรียกใช้ return bar (); // ฟังก์ชั่นที่สองนิพจน์ที่ไม่สามารถเข้าถึงได้} การแจ้งเตือน (foo ()); // 3คุณอาจพูดได้ว่าสิ่งนี้สามารถอธิบายได้ แต่คำตอบสำหรับคำถามที่ 3 นั้นผิด ฉันจะรายงานข้อผิดพลาดเมื่อเรียกใช้ Firebug
บันทึกรหัสในไฟล์ HTML และลองรันบน Firefox หรือเรียกใช้ใน IE8, Chrome หรือ Safari Console เห็นได้ชัดว่าคอนโซล Firebug จะไม่ส่งเสริมฟังก์ชั่นเมื่อเรียกใช้รหัสในขอบเขต "ทั่วโลก" (จริง ๆ แล้วไม่ใช่ระดับโลก แต่ขอบเขต "Firebug" ที่ไม่ซ้ำกัน - เพียงแค่พยายามเรียกใช้ "หน้าต่างนี้ == หน้าต่าง" ในคอนโซล Firebug)
คำถามที่ 3 และคำถามที่ 1 มีตรรกะที่คล้ายกัน คราวนี้ฟังก์ชั่น FOO ได้รับการส่งเสริม
คำถามที่ 4 นั้นง่ายมากไม่มีการปรับปรุงฟังก์ชั่นเลย ...
อาจกล่าวได้ว่า แต่ถ้าไม่มีการปรับปรุงเลย TypeError จะเป็น "บาร์ไม่ได้กำหนด" แทนที่จะเป็น "บาร์ไม่ใช่ฟังก์ชั่น" ไม่มีการปรับปรุงฟังก์ชั่นในตัวอย่างนี้ แต่มีการปรับปรุงตัวแปร ดังนั้นแถบจะถูกประกาศในตอนแรก แต่ไม่ได้กำหนดค่าของมัน รหัสอื่น ๆ จะถูกดำเนินการตามลำดับ
// ** ลำดับการประมวลผลแบบจำลองสำหรับคำถาม 4 ** ฟังก์ชั่น foo () {// การประกาศสำหรับแต่ละฟังก์ชันนิพจน์ var bar = ไม่ได้กำหนด; var bar = ไม่ได้กำหนด; Return Bar (); // typeError: "แถบไม่ได้กำหนด" // การแสดงออกของฟังก์ชั่นไม่ถึง} การแจ้งเตือน (foo ());คุณควรใส่ใจอะไรอีก?
อย่างเป็นทางการห้ามมิให้ใช้การประกาศฟังก์ชั่นในโมดูลที่ไม่ทำงาน (เช่น IF) อย่างไรก็ตามเบราว์เซอร์ทั้งหมดรองรับ แต่คำอธิบายของพวกเขาแตกต่างกัน
ตัวอย่างเช่นตัวอย่างโค้ดต่อไปนี้จะโยนข้อผิดพลาดใน Firefox 3.6 เนื่องจากตีความฟังก์ชันการประกาศฟังก์ชันเป็นคำสั่งฟังก์ชั่น (ดูด้านบน) ดังนั้น X จึงไม่ได้กำหนด X แต่ใน IE8, Chrome 5 และ Safari 5 ฟังก์ชั่น X จะถูกส่งคืน (เหมือนกับการประกาศฟังก์ชั่นมาตรฐาน)
function foo () {if (false) {function x () {}; } return x;} Alert (foo ());จะเห็นได้ว่าการใช้การประกาศฟังก์ชั่นอาจทำให้เกิดความสับสนดังนั้นจึงมีข้อดีหรือไม่?
คุณอาจบอกว่าการประกาศฟังก์ชั่นนั้นค่อนข้างหลวม - ถ้าคุณพยายามใช้ฟังก์ชั่นก่อนการประกาศโปรโมชั่นสามารถแก้ไขคำสั่งซื้อได้อย่างแน่นอนเพื่อให้ฟังก์ชั่นสามารถเรียกได้อย่างถูกต้อง แต่การคลายแบบนี้ไม่เอื้อต่อการเข้ารหัสที่เข้มงวดและจากมุมมองระยะยาวมันมีแนวโน้มที่จะส่งเสริมมากกว่าป้องกันอุบัติเหตุ ท้ายที่สุดมีเหตุผลที่โปรแกรมเมอร์จัดเรียงคำสั่งตามลำดับที่เฉพาะเจาะจง
ดังนั้นมีเหตุผลอื่น ๆ ในการสนับสนุนการแสดงออกของฟังก์ชั่นหรือไม่?
คุณคิดอย่างไร?
1) การประกาศฟังก์ชั่นรู้สึกเหมือนเป็นการเลียนแบบการประกาศวิธีการสไตล์ชวา แต่วิธี Java นั้นไม่เหมือนกับ JavaScript ใน JavaScript ฟังก์ชั่นเป็นวัตถุที่มีชีวิตที่มีค่า วิธี Java เป็นเพียงการจัดเก็บข้อมูลเมตา รหัสสองชิ้นต่อไปนี้กำหนดฟังก์ชั่น แต่เฉพาะนิพจน์ฟังก์ชั่นดูเหมือนว่าวัตถุถูกสร้างขึ้น
// ฟังก์ชั่นการประกาศฟังก์ชั่นเพิ่ม (a, b) {return a + b}; // function expressionvar add = function (a, b) {return a + b};2) ฟังก์ชั่นนิพจน์มีการใช้งานมากขึ้น การประกาศฟังก์ชั่นสามารถมีอยู่ในเด็กกำพร้าเป็น "คำสั่ง" เท่านั้น สิ่งที่ทำได้คือสร้างตัวแปรวัตถุภายใต้ขอบเขตปัจจุบัน การแสดงออกของฟังก์ชั่นโดยนิยามเป็นส่วนหนึ่งของโครงสร้างขนาดใหญ่ หากคุณต้องการสร้างฟังก์ชั่นที่ไม่ระบุชื่อให้เพิ่มฟังก์ชั่นลงในต้นแบบหรือใช้ฟังก์ชั่นเป็นคุณสมบัติของวัตถุอื่น ๆ คุณสามารถใช้นิพจน์ฟังก์ชั่น เมื่อใดก็ตามที่คุณใช้แอพพลิเคชั่นขั้นสูงเช่นแกงหรือเขียนคุณจะใช้นิพจน์ฟังก์ชั่นเมื่อสร้างฟังก์ชั่นใหม่ การแสดงออกของฟังก์ชั่นและการเขียนโปรแกรมฟังก์ชั่นนั้นแยกกันไม่ออก
// ฟังก์ชั่น ExpressionVar Sayshello = Alert.curry ("Hello!");มีข้อเสียในการแสดงออกของฟังก์ชันหรือไม่?
ฟังก์ชั่นนิพจน์สร้างฟังก์ชั่นส่วนใหญ่ไม่ระบุชื่อ ตัวอย่างเช่นฟังก์ชั่นต่อไปนี้ไม่ระบุชื่อวันนี้เป็นเพียงการอ้างอิงถึงฟังก์ชันที่ไม่ระบุชื่อ:
var today = function () {ส่งคืนวันที่ใหม่ ()}นี่จะเป็นปัญหาหรือไม่? ไม่ใช่ในกรณีส่วนใหญ่ แต่อย่างที่ Nick Fitzgerald ชี้ให้เห็นการดีบักฟังก์ชั่นนิรนามอาจน่ารำคาญ เขาแนะนำให้ใช้การแสดงออกของฟังก์ชั่นชื่อ (NFEs) เป็นพื้นที่ทำงาน:
var วันนี้ = ฟังก์ชั่นวันนี้ () {ส่งคืนวันที่ใหม่ ()}อย่างไรก็ตามดังที่ Asen Bozhilov กล่าวว่า (และเอกสาร Kangax) NFEs ไม่สามารถดำเนินการได้อย่างถูกต้องด้านล่าง IE9
สรุปแล้ว
การประกาศฟังก์ชั่นที่วางแบบสุ่มนั้นทำให้เข้าใจผิดและไม่ค่อยมีสถานการณ์ (ถ้ามี) และการกำหนดค่าให้กับตัวแปรที่มีการแสดงออกของฟังก์ชันไม่สามารถแทนที่การประกาศฟังก์ชั่นได้ แต่หากจำเป็นต้องมีการประกาศฟังก์ชั่นการวางไว้ที่ด้านบนของขอบเขตสามารถลดความสับสนได้ อย่าใส่การประกาศฟังก์ชั่นในคำสั่ง IF
ต้องพูดมากการประกาศฟังก์ชั่นอาจมีประโยชน์ในสถานการณ์ของคุณเอง มันไม่มีอะไร บทบาทความเชื่อนั้นอันตรายและมักจะทำให้รหัสถูกตีรอบพุ่มไม้ ที่สำคัญคุณเข้าใจแนวคิดเพื่อให้คุณสามารถตัดสินใจได้ว่าวิธีใดในการสร้างฟังก์ชั่นตามสถานการณ์ของคุณเอง ข้างต้นเป็นเนื้อหาทั้งหมดของบทความนี้ ฉันหวังว่าบทความนี้จะเป็นประโยชน์กับทุกคนในเรื่องนี้