diff --git a/1-js/02-first-steps/16-function-expressions/article.md b/1-js/02-first-steps/16-function-expressions/article.md index f414efdd6..fdb59a607 100644 --- a/1-js/02-first-steps/16-function-expressions/article.md +++ b/1-js/02-first-steps/16-function-expressions/article.md @@ -1,99 +1,95 @@ -# Function Expression +# นิพจน์ฟังก์ชัน -ในภาษา JavaScript ฟังก์ชันไม่ใช่แค่ "โครงสร้างภาษาที่พิเศษ" แต่ยังเป็นชนิดข้อมูลพิเศษอีกด้วย +ใน JavaScript ฟังก์ชันไม่ใช่ "โครงสร้างพิเศษทางภาษา" แต่เป็นค่าชนิดหนึ่งที่มีลักษณะเฉพาะ -ไวยากรณ์ที่เราเคยใช้มาก่อนหน้านี้เรียกว่า _Function Declaration_: +รูปแบบที่เราใช้ก่อนหน้านี้เรียกว่า *การประกาศฟังก์ชัน* (Function Declaration): ```js function sayHi() { - alert("สวัสดี"); + alert( "Hello" ); } ``` -นอกจากนี้ยังมีอีกรูปแบบหนึ่งในการสร้างฟังก์ชัน นั่นคือ _Function Expression_ +มีอีกวิธีหนึ่งในการสร้างฟังก์ชันที่เรียกว่า *นิพจน์ฟังก์ชัน* (Function Expression) -Function Expression ช่วยให้เราสามารถสร้างฟังก์ชันใหม่ได้ระหว่างนิพจน์ (expression) ใดๆ +ซึ่งช่วยให้เราสามารถสร้างฟังก์ชันใหม่ได้ในทุกๆ นิพจน์ (expression) ตัวอย่างเช่น: ```js let sayHi = function() { - alert("สวัสดี"); + alert( "Hello" ); }; ``` -ในที่นี้ เราจะเห็นว่าตัวแปร `sayHi` ได้รับการกำหนดค่าให้เป็นฟังก์ชันใหม่ ซึ่งถูกสร้างขึ้นมาเป็น `function() { alert("สวัสดี"); }` +ในที่นี้เราเห็นตัวแปร `sayHi` ได้รับค่าเป็นฟังก์ชันใหม่ที่สร้างขึ้นด้วย `function() { alert("Hello"); }` -เนื่องจากการสร้างฟังก์ชันนี้เกิดขึ้นในบริบทของนิพจน์กำหนดค่า (ทางขวาของเครื่องหมาย `=`) จึงเรียกว่าเป็น _Function Expression_ +เนื่องจากการสร้างฟังก์ชันเกิดขึ้นในบริบทของนิพจน์การกำหนดค่า (ทางขวาของเครื่องหมาย `=`) จึงเป็นนิพจน์ฟังก์ชัน -สังเกตได้ว่า ไม่มีการระบุชื่อต่อท้ายคีย์เวิร์ด `function` ซึ่งการละชื่อนี้สามารถทำได้กับ Function Expression +โปรดสังเกตว่าไม่มีชื่อหลังคำสำคัญ `function` การละเว้นชื่อนั้นอนุญาตสำหรับนิพจน์ฟังก์ชัน -ในตัวอย่างนี้ เรากำหนดค่าให้กับตัวแปรทันที นั่นหมายความว่า "สร้างฟังก์ชันขึ้นมาแล้วกำหนดลงในตัวแปร `sayHi`" +ในที่นี้เรากำหนดค่าให้กับตัวแปรทันที ดังนั้นความหมายของตัวอย่างโค้ดเหล่านี้จึงเหมือนกันคือ "สร้างฟังก์ชันและเก็บลงในตัวแปร `sayHi`" -ในสถานการณ์ที่ซับซ้อนกว่าซึ่งจะได้เจอในภายหลัง ฟังก์ชันอาจจะถูกสร้างและเรียกใช้งานทันที หรือถูกกำหนดเวลาให้รันภายหลัง โดยไม่จำเป็นต้องเก็บไว้ในตัวแปร ดังนั้นจึงมักจะไม่ระบุชื่อให้กับฟังก์ชันเหล่านี้ +ในสถานการณ์ที่ซับซ้อนขึ้นซึ่งเราจะเจอในภายหลัง ฟังก์ชันอาจถูกสร้างขึ้นและเรียกใช้ทันที หรือกำหนดเวลาเพื่อใช้ในภายหลัง โดยไม่เก็บไว้ที่ใดเลย จึงไม่มีชื่อ (anonymous) -## ฟังก์ชันก็คือค่าชนิดหนึ่ง +## ฟังก์ชันคือค่า -ขอย้ำอีกครั้งว่า ไม่ว่าจะสร้างฟังก์ชันด้วยวิธีใด มันก็ยังคงเป็นค่า (value) อยู่วันยังค่ำ ในทั้งสองตัวอย่างข้างต้น เราเก็บฟังก์ชันไว้ในตัวแปร `sayHi` +ขอย้ำอีกครั้ง: ไม่ว่าฟังก์ชันจะถูกสร้างมาอย่างไร มันคือค่าชนิดหนึ่ง ตัวอย่างทั้งสองด้านบนเก็บฟังก์ชันไว้ในตัวแปร `sayHi` -เราสามารถแสดงค่าของฟังก์ชันออกมาด้วยคำสั่ง `alert` ได้เลย: +เราสามารถแสดงค่านั้นผ่าน `alert` ได้ด้วย: ```js run function sayHi() { - alert("สวัสดี"); + alert( "Hello" ); } *!* -alert(sayHi); // แสดงโค้ดฟังก์ชัน +alert( sayHi ); // แสดงโค้ดฟังก์ชัน */!* ``` -สังเกตว่าบรรทัดสุดท้ายไม่ได้เรียกใช้ฟังก์ชัน เพราะไม่มีวงเล็บ `()` ต่อท้ายชื่อฟังก์ชัน `sayHi` ในบางภาษา การอ้างถึงชื่อฟังก์ชันอย่างเดียวจะเท่ากับการเรียกใช้มัน แต่ในจาวาสคริปต์ไม่เป็นเช่นนั้น +โปรดสังเกตว่าบรรทัดสุดท้ายไม่ได้เรียกใช้ฟังก์ชัน เพราะไม่มีวงเล็บหลัง `sayHi` มีภาษาโปรแกรมบางภาษาที่เมื่อพูดถึงชื่อฟังก์ชันจะทำให้เกิดการเรียกใช้ทันที แต่ JavaScript ไม่เป็นเช่นนั้น -ในจาวาสคริปต์ ฟังก์ชันถือเป็นค่าประเภทหนึ่ง เราจึงสามารถจัดการมันได้เหมือนกับค่าชนิดอื่นๆ ในโค้ดด้านบน เราแปลงค่าของฟังก์ชันเป็นสตริง ซึ่งก็คือโค้ดต้นฉบับของฟังก์ชันนั่นเอง +ใน JavaScript ฟังก์ชันเป็นค่าประเภทหนึ่ง เราจึงสามารถจัดการกับมันเหมือนกับค่าประเภทอื่นๆ โค้ดด้านบนแสดงการแปลงเป็นสตริง ซึ่งก็คือซอร์สโค้ดนั่นเอง -แน่นอนว่าฟังก์ชันเป็นค่าที่พิเศษกว่าค่าปกติทั่วไป ตรงที่เราสามารถเรียกใช้งานมันได้ เช่น `sayHi()` +แน่นอนว่าฟังก์ชันเป็นค่าพิเศษ ตรงที่เราสามารถเรียกใช้มันได้ เช่น `sayHi()` -แต่ถึงอย่างนั้น มันก็ยังคงความเป็นค่าชนิดหนึ่งอยู่ เราจึงสามารถจัดการมันได้เหมือนกับค่าประเภทอื่นๆ +แต่มันก็ยังคงเป็นค่าอยู่ เราจึงทำงานกับมันได้เหมือนกับค่าประเภทอื่นๆ -เราสามารถคัดลอกฟังก์ชันไปใส่ในตัวแปรอื่นได้: +เราสามารถคัดลอกฟังก์ชันไปยังตัวแปรอื่นได้: ```js run no-beautify -function sayHi() { // (1) สร้าง - alert("สวัสดี"); +function sayHi() { // (1) สร้าง + alert( "Hello" ); } -let func = sayHi; // (2) คัดลอก +let func = sayHi; // (2) คัดลอก -func(); // สวัสดี // (3) เรียกใช้ฟังก์ชันที่คัดลอกมา (ทำงานได้)! - -sayHi(); // สวัสดี // เรียกใช้ฟังก์ชันต้นฉบับก็ยังทำได้ +func(); // Hello // (3) เรียกใช้สำเนา (ใช้ได้)! +sayHi(); // Hello // ตัวต้นฉบับยังใช้ได้เหมือนเดิม (แน่นอน) ``` -มาดูว่าเกิดอะไรขึ้นในรายละเอียด: - -1. Function Declaration `(1)` สร้างฟังก์ชันและเก็บไว้ในตัวแปร `sayHi` +นี่คือสิ่งที่เกิดขึ้นในรายละเอียด: -2. บรรทัด `(2)` คัดลอกฟังก์ชันไปไว้ในตัวแปร `func` สังเกตว่าไม่มีวงเล็บ `()` ต่อท้ายชื่อ `sayHi` เพราะถ้ามี จะกลายเป็นเขียน _ผลลัพธ์_ จากการเรียก `sayHi()` ไปเก็บใน `func` ไม่ใช่เขียน _ฟังก์ชัน_ `sayHi` เอง +1. การประกาศฟังก์ชัน `(1)` สร้างฟังก์ชันและเก็บไว้ในตัวแปรชื่อ `sayHi` +2. บรรทัด `(2)` คัดลอกมันไปยังตัวแปร `func` โปรดสังเกตอีกครั้ง: ไม่มีวงเล็บหลัง `sayHi` ถ้ามี `func = sayHi()` จะเขียน *ผลลัพธ์จากการเรียกใช้* `sayHi()` ลงใน `func` ไม่ใช่เขียน *ฟังก์ชัน* `sayHi` เอง +3. ตอนนี้ฟังก์ชันสามารถเรียกได้ทั้งในชื่อ `sayHi()` และ `func()` -3. ตอนนี้ฟังก์ชันก็ถูกเรียกได้ทั้งในชื่อ `sayHi()` และ `func()` - -หรือเราอาจจะใช้ Function Expression ตั้งแต่ต้นก็ได้: +เราสามารถใช้นิพจน์ฟังก์ชันเพื่อประกาศ `sayHi` ในบรรทัดแรกก็ได้: ```js let sayHi = function() { // (1) สร้าง - alert("สวัสดี"); + alert( "Hello" ); }; let func = sayHi; // ... ``` -ทุกอย่างก็ยังทำงานได้เหมือนเดิม - -```smart header="ทำไมถึงมีเครื่องหมายอัฒภาคต่อท้าย?" +ทุกอย่างจะทำงานเหมือนเดิม -คุณอาจสงสัยว่าทำไม Function Expression ถึงมีเครื่องหมายอัฒภาค `;` ต่อท้าย ส่วน Function Declaration ไม่มี: +```smart header="ทำไมต้องมีเครื่องหมายอัฒภาคตามหลัง?" +คุณอาจสงสัยว่าทำไมนิพจน์ฟังก์ชันถึงต้องมีเครื่องหมายอัฒภาค `;` ท้ายประโยค แต่การประกาศฟังก์ชันไม่ต้องมี: ```js function sayHi() { @@ -105,27 +101,26 @@ let sayHi = function() { }*!*;*/!* ``` -คำตอบคือ เพราะ Function Expression ถูกสร้างภายในคำสั่งกำหนดค่าเป็น `function(...) {...}`: `let sayHi = ...;` ซึ่งจริงๆ แล้วเครื่องหมายอัฒภาค `;` เป็นสิ่งที่แนะนำให้ใส่ต่อท้ายคำสั่ง แต่ไม่ได้เป็นส่วนหนึ่งของไวยากรณ์ฟังก์ชัน +คำตอบคือ: นิพจน์ฟังก์ชันถูกสร้างขึ้นในรูปแบบ `function(…) {…}` ภายในประโยคการกำหนดค่า: `let sayHi = …;` ควรมีเครื่องหมายอัฒภาค `;` ท้ายประโยค มันไม่ใช่ส่วนหนึ่งของไวยากรณ์ฟังก์ชัน -ถ้าเป็นการกำหนดค่าแบบง่ายๆ เช่น `let sayHi = 5;` ก็ควรมีเครื่องหมายอัฒภาคอยู่ด้วย รวมถึงตอนกำหนดฟังก์ชันก็เช่นกัน -```` +เครื่องหมายอัฒภาคจะอยู่ในการกำหนดค่าทั่วไป เช่น `let sayHi = 5;` และอยู่ในการกำหนดค่าฟังก์ชันเช่นกัน ## ฟังก์ชันคอลแบ็ก -มาดูตัวอย่างเพิ่มเติมของการส่งฟังก์ชันเป็นค่าและการใช้ Function Expression กัน +มาดูตัวอย่างเพิ่มเติมเกี่ยวกับการส่งฟังก์ชันเป็นค่า และการใช้นิพจน์ฟังก์ชันกัน -เราจะลองเขียนฟังก์ชัน `ask(question, yes, no)` ที่รับพารามิเตอร์ 3 ตัว: +เราจะเขียนฟังก์ชัน `ask(question, yes, no)` ที่มีสามพารามิเตอร์: `question` -: ข้อความคำถาม +: ข้อความของคำถาม `yes` -: ฟังก์ชันที่จะทำงานเมื่อคำตอบคือ "ใช่" +: ฟังก์ชันที่จะรันหากคำตอบคือ "ใช่" `no` -: ฟังก์ชันที่จะทำงานเมื่อคำตอบคือ "ไม่" +: ฟังก์ชันที่จะรันหากคำตอบคือ "ไม่" -ฟังก์ชันนี้จะถามคำถาม `question` และเรียกใช้ `yes()` หรือ `no()` ขึ้นอยู่กับคำตอบที่ได้รับจากผู้ใช้: +ฟังก์ชันควรถาม `question` และเรียกใช้ `yes()` หรือ `no()` ตามคำตอบของผู้ใช้: ```js run *!* @@ -136,24 +131,24 @@ function ask(question, yes, no) { */!* function showOk() { - alert("คุณเห็นด้วย"); + alert( "You agreed." ); } function showCancel() { - alert("คุณยกเลิก"); + alert( "You canceled the execution." ); } -// วิธีเรียกใช้: ส่งฟังก์ชัน showOk, showCancel เป็นอาร์กิวเมนต์ให้ ask -ask("คุณเห็นด้วยไหม?", showOk, showCancel); +// การใช้งาน: ส่งฟังก์ชัน showOk, showCancel เป็นอาร์กิวเมนต์ให้ ask +ask("Do you agree?", showOk, showCancel); ``` -ในทางปฏิบัติ ฟังก์ชันแบบนี้ใช้ประโยชน์ได้มาก ความต่างหลักระหว่างฟังก์ชัน `ask` ในโลกจริงกับตัวอย่างด้านบนคือ ในโลกจริงฟังก์ชันมักใช้การโต้ตอบกับผู้ใช้ที่ซับซ้อนกว่า `confirm` ธรรมดา โดยเฉพาะในเบราว์เซอร์ที่ฟังก์ชันพวกนี้มักจะแสดงหน้าต่างคำถามที่สวยงาม แต่เรื่องนั้นเป็นอีกประเด็นหนึ่ง +ในทางปฏิบัติ ฟังก์ชันเหล่านี้มีประโยชน์มาก ความแตกต่างหลักระหว่าง `ask` ในชีวิตจริงกับตัวอย่างข้างต้นคือ ฟังก์ชันในชีวิตจริงใช้วิธีที่ซับซ้อนกว่าในการโต้ตอบกับผู้ใช้ นอกเหนือจากการใช้ `confirm` ธรรมดา ในเบราว์เซอร์ ฟังก์ชันเหล่านี้มักสร้างหน้าต่างคำถามที่สวยงาม แต่นั่นเป็นอีกเรื่องหนึ่ง -**อาร์กิวเมนต์ `showOk` และ `showCancel` ของฟังก์ชัน `ask` เรียกว่า *ฟังก์ชันคอลแบ็ก* หรือเรียกสั้นๆ ว่า *คอลแบ็ก*** +**อาร์กิวเมนต์ `showOk` และ `showCancel` ของ `ask` เรียกว่า *ฟังก์ชันคอลแบ็ก* หรือ *คอลแบ็ก*** -ไอเดียหลักๆ คือเราส่งฟังก์ชันไปเป็นค่า แล้วหวังว่ามันจะถูก "เรียกกลับมา" ในภายหลังเมื่อจำเป็น ในกรณีนี้ `showOk` กลายเป็นคอลแบ็กสำหรับคำตอบ "ใช่" และ `showCancel` สำหรับคำตอบ "ไม่" +แนวคิดคือ เราส่งฟังก์ชันไป และคาดหวังว่ามันจะถูก "เรียกกลับ (called back)" ในภายหลังหากจำเป็น ในกรณีของเรา `showOk` เป็นคอลแบ็กสำหรับคำตอบ "ใช่" และ `showCancel` สำหรับคำตอบ "ไม่" -เราสามารถเขียนโค้ดให้สั้นลงด้วยการใช้ Function Expression: +เราสามารถใช้นิพจน์ฟังก์ชันเพื่อเขียนฟังก์ชันเทียบเท่าที่สั้นกว่าได้: ```js run no-beautify function ask(question, yes, no) { @@ -163,65 +158,65 @@ function ask(question, yes, no) { *!* ask( - "คุณเห็นด้วยไหม?", - function() { alert("คุณเห็นด้วย"); }, - function() { alert("คุณยกเลิก"); } + "Do you agree?", + function() { alert("You agreed."); }, + function() { alert("You canceled the execution."); } ); */!* ``` -ตรงนี้ฟังก์ชันถูกประกาศไว้โดยตรงภายในการเรียกใช้ `ask(...)` โดยไม่มีการตั้งชื่อ เรียกว่าฟังก์ชันแบบ *ไม่ระบุชื่อ (anonymous)* ฟังก์ชันเหล่านี้จะเข้าถึงจากภายนอก `ask` ไม่ได้ (เพราะไม่ได้ผูกกับตัวแปรใด) แต่ในกรณีนี้เราก็ไม่ต้องการให้มันเข้าถึงได้อยู่แล้ว +ในที่นี้ ฟังก์ชันถูกประกาศขึ้นภายในการเรียก `ask(...)` โดยตรง โดยไม่มีการระบุชื่อ จึงเรียกว่า *anonymous (ไม่มีชื่อ)* ฟังก์ชันเหล่านี้ไม่สามารถเข้าถึงได้จากภายนอกของ `ask` (เพราะไม่ได้กำหนดให้กับตัวแปร) แต่นั่นแหละคือสิ่งที่เราต้องการในที่นี้ -โค้ดในลักษณะนี้มักจะเห็นได้บ่อยๆ ในสคริปต์ของพวกเรา ถือเป็นไปตามสไตล์การเขียนแบบ JavaScript +โค้ดลักษณะนี้ปรากฏในสคริปต์ของเราได้อย่างเป็นธรรมชาติ มันเป็นไปตามหลักการของภาษา JavaScript -```smart header="ฟังก์ชันคือค่าที่ใช้ในการแทน \"การกระทำ\"" -โดยปกติแล้ว ค่าอย่างสตริงหรือตัวเลขจะใช้แทน *ข้อมูล* +```smart header="ฟังก์ชันคือค่าที่แทนการ \"กระทำ\"" +ค่าปกติ เช่น สตริงหรือตัวเลข แสดงถึง *ข้อมูล* -ส่วนฟังก์ชันนั้น สามารถมองได้ว่าเป็น *การกระทำ* +สามารถมองฟังก์ชันเป็น *การกระทำ* ได้ -เราสามารถส่งมันเป็นค่าไปมาในตัวแปร และเรียกใช้มันได้เมื่อไหร่ก็ได้ที่ต้องการ +เราสามารถส่งผ่านมันไประหว่างตัวแปร และสั่งให้ทำงานเมื่อใดก็ได้ที่ต้องการ ``` -## Function Expression กับ Function Declaration +## นิพจน์ฟังก์ชัน (Function Expression) กับ การประกาศฟังก์ชัน (Function Declaration) -มาสรุปความแตกต่างหลักระหว่าง Function Declaration และ Function Expression กัน +มาสรุปความแตกต่างสำคัญระหว่างการประกาศฟังก์ชันและนิพจน์ฟังก์ชันกัน -ก่อนอื่น มาดูที่ไวยากรณ์: วิธีแยกแยะสองอย่างนี้ในโค้ด +ประการแรก ในแง่วากยสัมพันธ์: วิธีแยกแยะทั้งสองแบบในโค้ด -- *Function Declaration:* ฟังก์ชันที่ประกาศเป็นคำสั่งแยกเดี่ยวในโฟลว์หลักของโค้ด: +- *การประกาศฟังก์ชัน:* ฟังก์ชันที่ประกาศเป็นประโยคแยกต่างหาก ในเนื้อหาหลักของโค้ด: ```js - // Function Declaration + // การประกาศฟังก์ชัน function sum(a, b) { return a + b; } ``` -- *Function Expression:* ฟังก์ชันที่ถูกสร้างขึ้นภายในนิพจน์หรือโครงสร้างไวยากรณ์อื่นๆ ในตัวอย่างนี้ ฟังก์ชันถูกสร้างขึ้นทางด้านขวาของ "นิพจน์กำหนดค่า" `=`: +- *นิพจน์ฟังก์ชัน:* ฟังก์ชันที่ถูกสร้างภายในนิพจน์ หรือภายในโครงสร้างไวยากรณ์อื่นๆ ในที่นี้ ฟังก์ชันถูกสร้างขึ้นทางด้านขวาของ "นิพจน์การกำหนดค่า" `=`: ```js - // Function Expression + // นิพจน์ฟังก์ชัน let sum = function(a, b) { return a + b; }; ``` -ความแตกต่างที่ลึกซึ้งกว่านั้นคือ *เวลา* ที่ฟังก์ชันถูกสร้างขึ้นโดยเอ็นจิ้น JavaScript +ความแตกต่างที่ละเอียดอ่อนกว่าคือ เวลาที่ฟังก์ชันถูกสร้างโดยเอนจินของ JavaScript *เมื่อใด* -**Function Expression จะถูกสร้างขึ้นเมื่อการทำงานมาถึงบรรทัดนั้น และใช้ได้ตั้งแต่ตอนนั้นเป็นต้นไปเท่านั้น** +**นิพจน์ฟังก์ชัน ถูกสร้างเมื่อการประมวลผลมาถึงมัน และใช้งานได้ตั้งแต่จุดนั้นเป็นต้นไป** -เมื่อการประมวลผลไปถึงด้านขวาของการกำหนดค่า `let sum = function…` ตอนนี้ไงล่ะ ฟังก์ชันจะถูกสร้างขึ้น และสามารถนำไปใช้ได้ (เช่น กำหนดค่าให้ตัวแปร เรียกใช้งาน เป็นต้น) ตั้งแต่จุดนี้เป็นต้นไป +เมื่อโฟลว์การประมวลผลผ่านไปถึงทางด้านขวาของการกำหนดค่า `let sum = function…` -- ฟังก์ชันก็จะถูกสร้างขึ้น และสามารถใช้งานได้ตั้งแต่ตอนนี้เป็นต้นไป (กำหนดค่า เรียกใช้ ฯลฯ) -ในขณะที่ Function Declaration นั้นแตกต่างออกไป +การประกาศฟังก์ชันนั้นแตกต่างออกไป -**Function Declaration สามารถเรียกใช้ได้ก่อนที่จะมีการประกาศมันเสียอีก** +**การประกาศฟังก์ชันสามารถเรียกใช้ได้ก่อนที่จะมีการประกาศ** -ยกตัวอย่างเช่น Function Declaration ที่อยู่ในระดับโกลบอลจะมองเห็นได้ทั่วทั้งสคริปต์ ไม่ว่ามันจะถูกประกาศไว้ตรงไหนก็ตาม +ยกตัวอย่างเช่น การประกาศฟังก์ชันระดับโกลบอลจะมองเห็นได้ทั่วทั้งสคริปต์ ไม่ว่าจะอยู่ตรงไหน -นั่นเป็นเพราะกระบวนการภายใน ตอนที่ JavaScript เตรียมจะรันสคริปต์ มันจะค้นหา Function Declaration ในระดับโกลบอลก่อน แล้วสร้างฟังก์ชันเหล่านั้นขึ้น เราอาจมองเรียกมันว่า "ขั้นตอนเตรียมการ" +นั่นเป็นเพราะอัลกอริทึมภายใน เมื่อ JavaScript เตรียมตัวที่จะรันสคริปต์ มันจะมองหา การประกาศฟังก์ชันระดับโกลบอลก่อนเป็นอันดับแรก แล้วทำการสร้างฟังก์ชันเหล่านั้นขึ้น เราอาจมองเป็น "ขั้นตอนการเตรียมการ" -หลังจากที่ Function Declaration ทั้งหมดถูกประมวลผลเสร็จแล้ว การทำงานของโค้ดจึงเริ่มต้นขึ้น ดังนั้นจึงสามารถเข้าถึงฟังก์ชันเหล่านี้ได้ทันที +และหลังจากประมวลผล การประกาศฟังก์ชันทั้งหมดแล้ว จึงค่อยประมวลผลโค้ดตามปกติ ดังนั้นโค้ดจึงสามารถเข้าถึงฟังก์ชันเหล่านั้นได้ -ดังเช่นตัวอย่างนี้ที่ทำงานได้: +ตัวอย่างเช่น โค้ดนี้สามารถทำงานได้: ```js run refresh untrusted *!* @@ -233,34 +228,34 @@ function sayHi(name) { } ``` -Function Declaration `sayHi` จะถูกสร้างขึ้นตอนที่ JavaScript เตรียมจะเริ่มรันสคริปต์ และจะมองเห็นได้ทั่วทั้งสคริปต์ +การประกาศฟังก์ชัน `sayHi` ถูกสร้างขึ้นตอนที่ JavaScript กำลังเตรียมจะเริ่มสคริปต์ และมองเห็นได้ทั่วทั้งสคริปต์ -...แต่ถ้าเป็น Function Expression มันจะไม่สามารถทำงานแบบนี้ได้: +...แต่ถ้าเป็น นิพจน์ฟังก์ชันก็จะใช้งานไม่ได้: ```js run refresh untrusted *!* -sayHi("John"); // เกิด error! +sayHi("John"); // error! */!* -let sayHi = function(name) { // (*) ไม่มีเวทย์มนต์อีกต่อไป +let sayHi = function(name) { // (*) ไม่มีเวทมนต์อีกต่อไป alert( `Hello, ${name}` ); }; ``` -Function Expression จะถูกสร้างขึ้นเมื่อการทำงานมาถึงบรรทัดนั้น ซึ่งในที่นี้ก็คือบรรทัด `(*)` ซึ่งมันสายไปเสียแล้ว +นิพจน์ฟังก์ชันจะถูกสร้างก็ต่อเมื่อการประมวลผลมาถึงมัน ซึ่งจะเกิดขึ้นที่บรรทัด `(*)` เท่านั้น ช้าไปแล้ว -อีกหนึ่งคุณสมบัติพิเศษของ Function Declaration คือขอบเขตของบล็อก +อีกคุณสมบัติพิเศษของ การประกาศฟังก์ชันคือขอบเขตแบบบล็อก -**ในโหมดเข้มงวด ถ้า Function Declaration อยู่ภายในบล็อกโค้ด มันจะมองเห็นได้ทั่วทั้งภายในบล็อกนั้น แต่จะมองไม่เห็นจากภายนอกบล็อก** +**ในโหมดเข้มงวด เมื่อการประกาศฟังก์ชันอยู่ภายในบล็อกโค้ด มันจะมองเห็นได้ทั่วภายในบล็อกนั้น แต่มองไม่เห็นภายนอกบล็อก** -ยกตัวอย่างเช่น สมมติเราต้องการประกาศฟังก์ชัน `welcome()` โดยขึ้นอยู่กับตัวแปร `age` ที่ได้มาตอน runtime และวางแผนจะใช้มันภายหลัง +ยกตัวอย่าง สมมติเราต้องการประกาศฟังก์ชัน `welcome()` โดยขึ้นอยู่กับค่าตัวแปร `age` ที่ได้มาระหว่างรันไทม์ และเราวางแผนจะใช้มันในภายหลัง -ถ้าเราใช้ Function Declaration มันจะไม่ทำงานตามที่คาดหวัง: +หากใช้ การประกาศฟังก์ชันมันจะไม่ทำงานตามที่ตั้งใจ: ```js run let age = prompt("What is your age?", 18); -// ประกาศฟังก์ชันแบบมีเงื่อนไข +// ประกาศฟังก์ชันตามเงื่อนไข if (age < 18) { function welcome() { @@ -275,52 +270,17 @@ if (age < 18) { } -// ...วางแผนจะใช้ในภายหลัง -*!* -welcome(); // เกิด Error: welcome is not defined -*/!* -``` - -นั่นเป็นเพราะว่า Function Declaration จะมองเห็นได้เฉพาะภายในบล็อกโค้ดที่ประกาศมันเท่านั้น - -ลองดูตัวอย่างอื่นอีก: - -```js run -let age = 16; // สมมติใช้ 16 เป็นตัวอย่าง - -if (age < 18) { -*!* - welcome(); // \ (ทำงานได้) -*/!* - // | - function welcome() { // | Function Declaration พร้อมใช้งาน - alert("Hello!"); // | ได้ทุกที่ภายในบล็อกที่ประกาศมัน - } // | - // | -*!* - welcome(); // / (ทำงานได้) -*/!* - -} else { - - function welcome() { - alert("Greetings!"); - } -} - -// ตอนนี้เราอยู่นอกวงเล็บปีกกาแล้ว -// ดังนั้นจึงมองไม่เห็น Function Declaration ที่ประกาศภายในวงเล็บ - +// ...ใช้ในภายหลัง *!* -welcome(); // เกิด Error: welcome is not defined +welcome(); // Error: welcome is not defined */!* ``` -แล้วจะทำอย่างไรให้ `welcome` มองเห็นได้จากภายนอก `if` ล่ะ? +เราจะทำอย่างไรเพื่อให้ `welcome` มองเห็นได้จากภายนอก `if`? -วิธีที่ถูกต้องคือใช้ Function Expression แล้วกำหนดค่า `welcome` ให้กับตัวแปรที่ประกาศไว้ภายนอก `if` ซึ่งจะทำให้มองเห็นได้อย่างถูกต้อง +วิธีที่ถูกต้องคือการใช้ Function Expression และกำหนดค่า `welcome` ให้กับตัวแปรที่ประกาศไว้นอก `if` ซึ่งมีการมองเห็นที่เหมาะสม -ดังเช่นโค้ดนี้ที่ทำงานได้ตามที่ตั้งใจ: +โค้ดนี้ทำงานตามที่ตั้งใจไว้: ```js run let age = prompt("What is your age?", 18); @@ -342,11 +302,11 @@ if (age < 18) { } *!* -welcome(); // ตอนนี้ ok แล้ว +welcome(); // ตอนนี้ใช้ได้แล้ว */!* ``` -หรือจะทำให้เรียบง่ายยิ่งขึ้นโดยใช้ตัวดำเนินการ `?`: +หรือเราสามารถทำให้มันเรียบง่ายขึ้นอีกโดยใช้ตัวดำเนินการเครื่องหมายคำถาม `?`: ```js run let age = prompt("What is your age?", 18); @@ -356,31 +316,27 @@ let welcome = (age < 18) ? function() { alert("Greetings!"); }; *!* -welcome(); // ตอนนี้ ok แล้ว +welcome(); // ตอนนี้ใช้ได้แล้ว */!* ``` -```smart header="เมื่อไหร่ควรเลือกใช้ Function Declaration กับ Function Expression?" -โดยทั่วไปแล้ว ถ้าเป็นไปได้ควรเลือกใช้ Function Declaration มันให้อิสระในการจัดระเบียบโค้ดมากกว่า เพราะเราสามารถเรียกฟังก์ชันพวกนี้ได้ก่อนที่จะประกาศมัน +```smart header="เมื่อไหร่ควรเลือกใช้การประกาศฟังก์ชัน หรือนิพจน์ฟังก์ชัน?" +โดยหลักการทั่วไป เมื่อเราต้องการประกาศฟังก์ชัน สิ่งแรกที่ควรพิจารณาคือใช้ไวยากรณ์แบบการประกาศฟังก์ชัน เพราะมันให้อิสระในการจัดวางโค้ดมากกว่า เนื่องจากเราสามารถเรียกใช้ฟังก์ชันเหล่านั้นได้ก่อนที่จะมีการประกาศ -นอกจากนี้ยังทำให้โค้ดอ่านง่ายขึ้นด้วย เพราะการมองหา `function f(…) {…}` ในโค้ดนั้นง่ายกว่า `let f = function(…) {…};` Function Declaration จะ "เด่น" กว่านั่นเอง +นอกจากนี้ยังอ่านทำความเข้าใจง่ายกว่า เพราะมองหา `function f(…) {…}` ในโค้ดได้ง่ายกว่า `let f = function(…) {…};` การประกาศฟังก์ชันนั้น "โดดเด่นมากกว่า" -...แต่ถ้า Function Declaration ไม่เหมาะกับเราด้วยเหตุผลบางอย่าง หรือถ้าเราอยากประกาศฟังก์ชันแบบมีเงื่อนไข (อย่างในตัวอย่างที่เราเห็นไปเมื่อกี้) การใช้ Function Expression ก็จะเหมาะสมกว่า +...แต่ถ้าการประกาศฟังก์ชันไม่เหมาะสมกับเราด้วยเหตุผลบางประการ หรือเราต้องการประกาศแบบมีเงื่อนไข (เราเพิ่งเห็นตัวอย่างไป) ควรใช้นิพจน์ฟังก์ชันแทน ``` ## สรุป -- ฟังก์ชันคือค่าชนิดหนึ่ง สามารถกำหนดค่า คัดลอก หรือประกาศไว้ในตำแหน่งใดๆ ของโค้ดก็ได้ - -- ถ้าฟังก์ชันถูกประกาศเป็นคำสั่งแยกเดี่ยวในโฟลว์หลักของโค้ด จะเรียกว่า "Function Declaration" - -- ถ้าฟังก์ชันถูกสร้างขึ้นเป็นส่วนหนึ่งของนิพจน์ จะเรียกว่า "Function Expression" - -- Function Declaration จะถูกประมวลผลก่อนที่บล็อกโค้ดจะเริ่มทำงาน ทำให้มองเห็นได้ทั่วทั้งบล็อก - -- Function Expression จะถูกสร้างขึ้นเมื่อลำดับการทำงานมาถึงบรรทัดนั้น +- ฟังก์ชันคือค่า สามารถกำหนดค่า คัดลอก หรือประกาศไว้ที่ใดก็ได้ในโค้ด +- ถ้าฟังก์ชันถูกประกาศเป็นประโยคแยกต่างหากในลำดับการทำงานหลักของโค้ด เรียกว่า "Function Declaration" +- ถ้าฟังก์ชันถูกสร้างขึ้นเป็นส่วนหนึ่งของนิพจน์ เรียกว่า "Function Expression" +- Function Declaration จะถูกประมวลผลก่อนที่บล็อกโค้ดจะเริ่มทำงาน ทำให้มองเห็นได้ทั่วทั้งบล็อก +- Function Expression จะถูกสร้างขึ้นเมื่อลำดับการทำงานมาถึงจุดที่มันอยู่ -ในกรณีส่วนใหญ่ที่เราต้องการประกาศฟังก์ชัน การใช้ Function Declaration จะเป็นตัวเลือกที่ดีกว่า เพราะมันมองเห็นได้ก่อนที่จะถูกประกาศ ทำให้เรามีความยืดหยุ่นในการจัดวางโค้ดมากขึ้น และมักจะอ่านเข้าใจได้ง่ายกว่า +ในเกือบทุกกรณีที่เราต้องการประกาศฟังก์ชัน Function Declaration เป็นตัวเลือกที่เหมาะสมกว่า เพราะมันมองเห็นได้ก่อนการประกาศจริงๆ ทำให้เรามีความยืดหยุ่นในการจัดวางโค้ดมากขึ้น และมักจะอ่านเข้าใจง่ายกว่า -ดังนั้น เราควรใช้ Function Expression ก็ต่อเมื่อ Function Declaration ไม่ตอบโจทย์งานของเรา ซึ่งเราได้เห็นตัวอย่างสองสามกรณีไปแล้วในบทนี้ และจะเห็นเพิ่มเติมอีกในอนาคต \ No newline at end of file +ดังนั้นเราควรใช้ Function Expression เฉพาะเมื่อ Function Declaration ไม่เหมาะสมกับงาน เราได้เห็นตัวอย่างไปบ้างแล้วในบทนี้ และจะเห็นเพิ่มเติมในอนาคต \ No newline at end of file