ใน JavaScript ฟังก์ชันไม่ใช่ "โครงสร้างภาษามหัศจรรย์" แต่เป็นค่าชนิดพิเศษ
ไวยากรณ์ที่เราใช้ก่อนหน้านี้เรียกว่า Function Declaration :
ฟังก์ชั่น sayHi() { alert( "สวัสดี" ); -
มีไวยากรณ์อื่นสำหรับการสร้างฟังก์ชันที่เรียกว่า Function Expression
มันช่วยให้เราสามารถสร้างฟังก์ชั่นใหม่ในช่วงกลางของการแสดงออกใดๆ
ตัวอย่างเช่น:
ให้ sayHi = function() { alert( "สวัสดี" ); -
ที่นี่เราจะเห็นตัวแปร sayHi
รับค่า ซึ่งเป็นฟังก์ชันใหม่ที่สร้างขึ้นเป็น function() { alert("Hello"); }
.
เนื่องจากการสร้างฟังก์ชันเกิดขึ้นในบริบทของนิพจน์การกำหนด (ทางด้านขวาของ =
) นี่คือ Function Expression
โปรดทราบว่าไม่มีชื่อหลังคีย์เวิร์ด function
อนุญาตให้ละเว้นชื่อสำหรับนิพจน์ฟังก์ชัน
ที่นี่เรากำหนดมันให้กับตัวแปรทันที ดังนั้นความหมายของตัวอย่างโค้ดเหล่านี้จึงเหมือนกัน: “สร้างฟังก์ชันและใส่ลงในตัวแปร sayHi
”
ในสถานการณ์ขั้นสูงที่เราจะเจอในภายหลัง ฟังก์ชันอาจถูกสร้างขึ้นและเรียกทันทีหรือกำหนดเวลาสำหรับการดำเนินการในภายหลัง โดยไม่จัดเก็บไว้ที่ใดเลย จึงไม่เปิดเผยตัวตน
ขอย้ำอีกครั้งว่า ไม่ว่าฟังก์ชันจะถูกสร้างขึ้นมาอย่างไร ฟังก์ชันก็คือค่า ทั้งสองตัวอย่างด้านบนจัดเก็บฟังก์ชันไว้ในตัวแปร sayHi
เรายังสามารถพิมพ์ค่านั้นโดยใช้ alert
:
ฟังก์ชั่น sayHi() { alert( "สวัสดี" ); - แจ้งเตือน( สวัสดี ); // แสดงโค้ดฟังก์ชัน
โปรดทราบว่าบรรทัดสุดท้ายจะไม่เรียกใช้ฟังก์ชันนี้ เนื่องจากไม่มีวงเล็บหลัง sayHi
มีภาษาการเขียนโปรแกรมที่มีการกล่าวถึงชื่อฟังก์ชันทำให้เกิดการดำเนินการ แต่ JavaScript ไม่ใช่เช่นนั้น
ใน JavaScript ฟังก์ชันคือค่า ดังนั้นเราจึงสามารถจัดการกับมันเป็นค่าได้ โค้ดด้านบนแสดงการแสดงสตริงซึ่งเป็นซอร์สโค้ด
แน่นอนว่าฟังก์ชันนั้นเป็นค่าพิเศษ ในแง่ที่เราเรียกมันว่า sayHi()
ได้
แต่มันก็ยังคงเป็นคุณค่า ดังนั้นเราจึงสามารถทำงานร่วมกับค่านิยมประเภทอื่นๆ ได้
เราสามารถคัดลอกฟังก์ชันไปยังตัวแปรอื่นได้:
ฟังก์ชั่น sayHi() { // (1) สร้าง alert( "สวัสดี" ); - ให้ func = พูดสวัสดี; // (2) สำเนา ฟังก์ชั่น(); // สวัสดี // (3) เรียกใช้สำเนา (ใช้งานได้)! พูดว่าสวัสดี(); // สวัสดี // นี่ก็ยังใช้ได้อยู่เหมือนกัน (ทำไมจะไม่ได้ล่ะ)
ต่อไปนี้คือสิ่งที่เกิดขึ้นโดยละเอียดข้างต้น:
การประกาศฟังก์ชัน (1)
สร้างฟังก์ชันและวางลงในตัวแปรชื่อ sayHi
บรรทัด (2)
คัดลอกลงในตัวแปร func
โปรดทราบอีกครั้ง: หลังจาก sayHi
ไม่มีวงเล็บ หากมี func = sayHi()
จะเขียน ผลลัพธ์ของการเรียก sayHi()
ลงใน func
ไม่ใช่ ฟังก์ชัน sayHi
เอง
ตอนนี้สามารถเรียกฟังก์ชันเป็นทั้ง sayHi()
และ func()
ได้
เรายังสามารถใช้ Function Expression เพื่อประกาศ sayHi
ในบรรทัดแรก:
ให้ sayHi = function() { // (1) สร้าง alert( "สวัสดี" ); - ให้ func = พูดสวัสดี; -
ทุกอย่างจะทำงานเหมือนกัน
ทำไมถึงมีเครื่องหมายอัฒภาคต่อท้าย?
คุณอาจสงสัยว่าเหตุใด Function Expressions จึงมีอัฒภาค ;
ในตอนท้าย แต่การประกาศฟังก์ชันไม่ได้:
ฟังก์ชั่น sayHi() { - - ให้ sayHi = function() { - -
คำตอบนั้นง่ายมาก: Function Expression ถูกสร้างขึ้นที่นี่เป็น function(…) {…}
ภายในคำสั่งการมอบหมาย: let sayHi = …;
- อัฒภาค ;
แนะนำให้ใช้ที่ส่วนท้ายของคำสั่ง ไม่ใช่ส่วนหนึ่งของไวยากรณ์ของฟังก์ชัน
อัฒภาคจะอยู่ที่นั่นเพื่อการมอบหมายที่ง่ายกว่า เช่น let sayHi = 5;
และยังมีไว้สำหรับการมอบหมายฟังก์ชันด้วย
ลองดูตัวอย่างเพิ่มเติมของการส่งฟังก์ชันเป็นค่าและการใช้นิพจน์ฟังก์ชัน
เราจะเขียนฟังก์ชัน ask(question, yes, no)
โดยมีพารามิเตอร์ 3 ตัว:
question
ข้อความของคำถาม
yes
ฟังก์ชั่นให้ทำงานหากคำตอบคือ "ใช่"
no
ฟังก์ชั่นให้ทำงานหากคำตอบคือ "ไม่"
ฟังก์ชั่นควรถาม question
และโทร yes()
หรือ no()
: ขึ้นอยู่กับคำตอบของผู้ใช้
ฟังก์ชั่นถาม (คำถาม ใช่ ไม่ใช่) { ถ้า (ยืนยัน (คำถาม)) ใช่ () อย่างอื่นไม่มี(); - ฟังก์ชั่น showOk() { alert( "คุณเห็นด้วย" ); - ฟังก์ชั่น showCancel() { alert( "คุณยกเลิกการดำเนินการ" ); - // การใช้งาน: ฟังก์ชั่น showOk, showCancel ถูกส่งเป็นอาร์กิวเมนต์ที่จะถาม ถาม ("คุณเห็นด้วยหรือไม่?", showOk, showCancel);
ในทางปฏิบัติฟังก์ชั่นดังกล่าวมีประโยชน์ค่อนข้างมาก ความแตกต่างที่สำคัญระหว่าง ask
ในชีวิตจริงกับตัวอย่างข้างต้นก็คือ ฟังก์ชันในชีวิตจริงใช้วิธีการที่ซับซ้อนในการโต้ตอบกับผู้ใช้มากกว่า confirm
แบบง่ายๆ ในเบราว์เซอร์ ฟังก์ชันดังกล่าวมักจะวาดหน้าต่างคำถามที่ดูดี แต่นั่นเป็นอีกเรื่องหนึ่ง
อาร์กิวเมนต์ showOk
และ showCancel
ของ ask
เรียกว่า ฟังก์ชันการโทรกลับ หรือเพียงแค่ การโทรกลับ
แนวคิดก็คือเราจะผ่านฟังก์ชันและคาดหวังว่าฟังก์ชันนั้นจะถูก “เรียกกลับ” ในภายหลังหากจำเป็น ในกรณีของเรา showOk
จะกลายเป็นการโทรกลับสำหรับคำตอบ "ใช่" และ showCancel
สำหรับคำตอบ "ไม่"
เราสามารถใช้ Function Expressions เพื่อเขียนฟังก์ชันที่เทียบเท่าและสั้นกว่าได้:
ฟังก์ชั่นถาม (คำถาม ใช่ ไม่ใช่) { ถ้า (ยืนยัน (คำถาม)) ใช่ () อย่างอื่นไม่มี(); - ถาม( “คุณเห็นด้วยไหม”, function() { alert("คุณตกลง"); - function() { alert("คุณยกเลิกการดำเนินการ"); - -
ที่นี่ ฟังก์ชันต่างๆ จะถูกประกาศไว้ภายในการเรียก ask(...)
พวกเขาไม่มีชื่อ จึงถูกเรียกว่า ไม่ระบุชื่อ ฟังก์ชั่นดังกล่าวไม่สามารถเข้าถึงได้นอกเหนือจาก ask
(เนื่องจากไม่ได้ถูกกำหนดให้กับตัวแปร) แต่นั่นคือสิ่งที่เราต้องการที่นี่
โค้ดดังกล่าวปรากฏในสคริปต์ของเราอย่างเป็นธรรมชาติ โดยอยู่ในจิตวิญญาณของ JavaScript
ฟังก์ชั่นคือค่าที่แสดงถึง "การกระทำ"
ค่าปกติ เช่น สตริงหรือตัวเลขแสดงถึง ข้อมูล
ฟังก์ชั่นสามารถรับรู้ได้ว่าเป็นการ กระทำ
เราสามารถส่งผ่านระหว่างตัวแปรและรันเมื่อเราต้องการ
มากำหนดความแตกต่างที่สำคัญระหว่างการประกาศฟังก์ชันและนิพจน์กัน
ประการแรก ไวยากรณ์: วิธีแยกความแตกต่างระหว่างพวกเขาในโค้ด
การประกาศฟังก์ชัน: ฟังก์ชันที่ประกาศเป็นคำสั่งแยกต่างหากในโฟลว์โค้ดหลัก:
// ประกาศฟังก์ชั่น ผลรวมฟังก์ชัน (a, b) { กลับ + b; -
นิพจน์ฟังก์ชัน: ฟังก์ชันที่สร้างขึ้นภายในนิพจน์หรือภายในโครงสร้างไวยากรณ์อื่น ที่นี่ ฟังก์ชันจะถูกสร้างขึ้นทางด้านขวาของ “นิพจน์การกำหนด” =
:
// นิพจน์ฟังก์ชัน ให้ผลรวม = ฟังก์ชั่น (a, b) { กลับ + b; -
ความแตกต่างที่ลึกซึ้งยิ่งขึ้นคือ เมื่อ ฟังก์ชันถูกสร้างขึ้นโดยเอ็นจิ้น JavaScript
Function Expression จะถูกสร้างขึ้นเมื่อมีการดำเนินการถึงจุดดังกล่าว และจะสามารถใช้งานได้นับจากช่วงเวลานั้นเท่านั้น
เมื่อโฟลว์การดำเนินการส่งผ่านไปยังด้านขวาของการกำหนด let sum = function…
– เอาล่ะ ฟังก์ชันจะถูกสร้างขึ้นและสามารถใช้งานได้ (มอบหมาย เรียกใช้ ฯลฯ ) นับจากนี้เป็นต้นไป
การประกาศฟังก์ชันจะแตกต่างกัน
การประกาศฟังก์ชันสามารถเรียกได้เร็วกว่าที่กำหนดไว้
ตัวอย่างเช่น การประกาศฟังก์ชันส่วนกลางจะปรากฏให้เห็นในสคริปต์ทั้งหมด ไม่ว่าจะอยู่ที่ใดก็ตาม
นั่นเป็นเพราะอัลกอริทึมภายใน เมื่อ JavaScript เตรียมรันสคริปต์ ขั้นแรก JavaScript จะมองหาการประกาศฟังก์ชันส่วนกลางในสคริปต์และสร้างฟังก์ชันต่างๆ เราอาจมองว่ามันเป็น "ขั้นตอนการเริ่มต้น"
และหลังจากประมวลผลการประกาศฟังก์ชันทั้งหมดแล้ว รหัสก็จะถูกดำเนินการ ดังนั้นจึงสามารถเข้าถึงฟังก์ชันเหล่านี้ได้
ตัวอย่างเช่น วิธีนี้ได้ผล:
sayHi("จอห์น"); //สวัสดีจอห์น ฟังก์ชั่น sayHi (ชื่อ) { alert( `สวัสดี ${name}` ); -
การประกาศฟังก์ชัน sayHi
ถูกสร้างขึ้นเมื่อ JavaScript กำลังเตรียมที่จะเริ่มสคริปต์และมองเห็นได้ทุกที่ในสคริปต์
…ถ้าเป็น Function Expression มันจะไม่ทำงาน:
sayHi("จอห์น"); // ข้อผิดพลาด! ให้ sayHi = function(name) { // (*) ไม่มีเวทย์มนตร์อีกต่อไป alert( `สวัสดี ${name}` ); -
นิพจน์ฟังก์ชันจะถูกสร้างขึ้นเมื่อมีการดำเนินการถึงพวกเขา ที่จะเกิดขึ้นเฉพาะในบรรทัด (*)
สายเกินไป.
คุณสมบัติพิเศษอีกอย่างหนึ่งของการประกาศฟังก์ชันคือขอบเขตบล็อก
ในโหมดเข้มงวด เมื่อการประกาศฟังก์ชันอยู่ภายในบล็อกโค้ด จะมองเห็นได้ทุกที่ภายในบล็อกนั้น แต่ไม่ใช่นอกนั้น
ตัวอย่างเช่น ลองจินตนาการว่าเราจำเป็นต้องประกาศฟังก์ชัน welcome()
ขึ้นอยู่กับตัวแปร age
ที่เราได้รับระหว่างรันไทม์ จากนั้นเราวางแผนที่จะใช้มันในภายหลัง
หากเราใช้ Function Declaration มันจะไม่ทำงานตามที่ตั้งใจไว้:
ให้อายุ = prompt ("คุณอายุเท่าไร", 18); // ประกาศฟังก์ชันแบบมีเงื่อนไข ถ้า (อายุ < 18) { ฟังก์ชั่นยินดีต้อนรับ () { alert("สวัสดี!"); - } อื่น { ฟังก์ชั่นยินดีต้อนรับ () { alert("สวัสดี!"); - - // ...ใช้ทีหลัง ยินดีต้อนรับ(); // ข้อผิดพลาด: ไม่ได้กำหนดไว้ ยินดีต้อนรับ
นั่นเป็นเพราะว่าการประกาศฟังก์ชันจะมองเห็นได้เฉพาะภายในบล็อคโค้ดที่มันอยู่เท่านั้น
นี่เป็นอีกตัวอย่างหนึ่ง:
ให้อายุ = 16; // เอา 16 เป็นตัวอย่าง ถ้า (อายุ < 18) { ยินดีต้อนรับ(); // (วิ่ง) - ฟังก์ชั่นยินดีต้อนรับ () { // | alert("สวัสดี!"); - มีการประกาศฟังก์ชันแล้ว - ทุกที่ในบล็อกที่มีการประกาศ - ยินดีต้อนรับ(); // / (วิ่ง) } อื่น { ฟังก์ชั่นยินดีต้อนรับ () { alert("สวัสดี!"); - - // นี่เราไม่มีวงเล็บปีกกาแล้ว // ดังนั้นเราจึงไม่สามารถเห็นการประกาศฟังก์ชันภายในนั้นได้ ยินดีต้อนรับ(); // ข้อผิดพลาด: ไม่ได้กำหนดไว้ ยินดีต้อนรับ
เราจะทำอย่างไรเพื่อให้ welcome
มองเห็นได้ภายนอก if
?
วิธีที่ถูกต้องคือการใช้ Function Expression และกำหนดการ welcome
ให้กับตัวแปรที่ประกาศไว้ภายนอก if
และมีการมองเห็นที่เหมาะสม
รหัสนี้ทำงานตามที่ตั้งใจไว้:
ให้อายุ = prompt ("คุณอายุเท่าไร", 18); ยินดีต้อนรับ; ถ้า (อายุ < 18) { ยินดีต้อนรับ = ฟังก์ชั่น () { alert("สวัสดี!"); - } อื่น { ยินดีต้อนรับ = ฟังก์ชั่น () { alert("สวัสดี!"); - - ยินดีต้อนรับ(); // ตกลงตอนนี้
หรือเราสามารถทำให้มันง่ายขึ้นอีกโดยใช้โอเปอเรเตอร์เครื่องหมายคำถาม ?
-
ให้อายุ = prompt ("คุณอายุเท่าไร", 18); ยินดีต้อนรับ = (อายุ < 18) ? ฟังก์ชั่น() { การแจ้งเตือน ("สวัสดี!"); - function() { alert("สวัสดี!"); - ยินดีต้อนรับ(); // ตกลงตอนนี้
เมื่อใดจึงควรเลือกการประกาศฟังก์ชันกับนิพจน์ฟังก์ชัน
ตามกฎทั่วไป เมื่อเราต้องการประกาศฟังก์ชัน สิ่งแรกที่ต้องพิจารณาคือไวยากรณ์การประกาศฟังก์ชัน มันให้อิสระมากขึ้นในการจัดระเบียบโค้ดของเรา เนื่องจากเราสามารถเรียกใช้ฟังก์ชันดังกล่าวก่อนที่จะมีการประกาศ
นอกจากนี้ ยังช่วยให้อ่านง่ายขึ้น เนื่องจากค้นหา function f(…) {…}
ในโค้ดได้ง่ายกว่า let f = function(…) {…};
- การประกาศฟังก์ชันมีความ "สะดุดตา" มากกว่า
…แต่ถ้า Function Declaration ไม่เหมาะกับเราด้วยเหตุผลบางประการ หรือเราต้องการการประกาศแบบมีเงื่อนไข (เราเพิ่งเห็นตัวอย่าง) ก็ควรใช้ Function Expression
ฟังก์ชันคือค่า สามารถกำหนด คัดลอก หรือประกาศในตำแหน่งใดก็ได้ของโค้ด
หากฟังก์ชันถูกประกาศเป็นคำสั่งแยกต่างหากในโฟลว์โค้ดหลัก นั่นเรียกว่า “การประกาศฟังก์ชัน”
หากฟังก์ชันถูกสร้างขึ้นโดยเป็นส่วนหนึ่งของนิพจน์ จะเรียกว่า "นิพจน์ฟังก์ชัน"
การประกาศฟังก์ชันจะถูกประมวลผลก่อนดำเนินการบล็อกโค้ด มองเห็นได้ทุกที่ในบล็อก
นิพจน์ฟังก์ชันจะถูกสร้างขึ้นเมื่อโฟลว์การดำเนินการมาถึง
ในกรณีส่วนใหญ่ เมื่อเราต้องการประกาศฟังก์ชัน การประกาศฟังก์ชันจะดีกว่า เนื่องจากสามารถมองเห็นได้ก่อนการประกาศนั่นเอง นั่นทำให้เรามีความยืดหยุ่นมากขึ้นในการจัดระเบียบโค้ดและมักจะอ่านง่ายกว่า
ดังนั้นเราควรจะใช้ Function Expression เฉพาะเมื่อ Function Declaration ไม่เหมาะกับงานเท่านั้น เราได้เห็นตัวอย่างบางส่วนแล้วในบทนี้ และจะได้เห็นเพิ่มเติมในอนาคต