ต้องออกตัวก่อนว่าผมเองไม่ใช่ผู้เชี่ยวชาญภาษา JavaScript แต่อย่างใด เคยเขียนบ้างนิดๆหน่อยๆ แต่ด้วยความที่ไวยากรณ์ (Syntax) ของมันคล้ายกับภาษา C, Java ที่ผมพอจะคุ้นเคยอยู่ เวลาผมเห็นโค้ด JavaScript ก็เลยไม่รู้สึกลำบากอะไร และสามารถทำความเข้าใจมันได้ระดับนึง
วันก่อนผมไปอ่านบทความของฝรั่งที่อธิบายเรื่อง Closure ใน JavaScript ซึ่งว่ากันว่าเป็นเรื่องที่เข้าใจยากเรื่องหนึ่งของภาษานี้ ผมเห็นว่าน่าสนใจดี ก็เลยอยากจะนำมาถ่ายทอดให้เข้าใจง่ายๆตามสไตล์ของผมบ้าง จึงเป็นที่มาของบทความนี้ครับ
รู้จักกับนิพจน์ฟังก์ชั่น (Function Expression)
ก่อนจะอธิบายเรื่อง Closure คุณต้องเข้าใจสิ่งที่เรียกว่า “นิพจน์ฟังก์ชั่น” (Function Expression) ใน JavaScript เสียก่อนโดยทั่วไปเวลาเราต้องการสร้างฟังก์ชั่นไว้เรียกใช้งาน เราจะกำหนด (define) ฟังก์ชั่นในลักษณะนี้ใช่ไหมครับ
หลังจากกำหนดฟังก์ชั่น sayHello2 ลงในตัวแปร f แล้ว เวลาเรียกใช้ฟังก์ชั่นก็จะเรียกผ่านตัวแปร f แบบนี้
และเนื่องจากการเรียกใช้นิพจน์ฟังก์ชั่น เราไม่ได้เรียกจากชื่อของมัน แต่เรียกจากตัวแปรที่อ้างอิงมัน โดยทั่วไปเราจึงมักไม่ตั้งชื่อให้มัน
แถมเรื่องนิพจน์ฟังก์ชั่นอีกนิด (อันนี้ค่อนข้างนอกเรื่อง Closure ไปหน่อยนะครับ) ถ้าคุณต้องการสร้างนิพจน์ฟังก์ชั่นแล้วเรียกใช้มันตรงนั้นทันที แทนที่จะเก็บลงตัวแปรก่อนแล้วเรียกผ่านตัวแปร ก็ให้เอาวงเล็บมาคลุมนิพจน์ฟังก์ชั่นทั้งหมดไว้ แล้วตามด้วยวงเล็บเปิด-เปิด เช่น
ถ้าหากนิพจน์ฟังก์ชั่นมีพารามิเตอร์ การสร้างและเรียกใช้ทันทีจะเขียนได้ดังนี้
Closure คืออะไร?
เอาล่ะครับ คราวนี้ก็มาที่ประเด็นหลักที่ผมจะอธิบายในบทความนี้ นั่นคือเรื่อง Closure คุณอาจสงสัยว่ามันคืออะไร และอยากให้ผมอธิบายความหมายของมันเดี๋ยวนี้เลยแต่ช้าก่อนครับ ถึงผมจะบอกความหมายตอนนี้ เชื่อเถอะว่าคุณก็อาจไม่เข้าใจอยู่ดี เรื่อง Closure นั้นคุณต้องดูตัวอย่างโค้ดไปเรื่อยๆครับ จึงจะเข้าใจอย่างแท้จริงว่ามันคืออะไร
จุดเริ่มต้นของ Closure มาจากการที่ภาษา JavaScript อนุญาตให้เราสร้างฟังก์ชั่นซ้อนฟังก์ชั่นได้ ดังตัวอย่าง
อย่างไรก็ตาม การสร้างฟังก์ชั่นซ้อนกันในลักษณะนี้ยังไม่ใช่ Closure (จริงๆต้องบอกว่ายังไม่เกิด Closure) แต่ Closure จะเกิดขึ้นเมื่อเราสร้างนิพจน์ฟังก์ชั่นซ้อนไว้ข้างในฟังก์ชั่นอื่น แล้วมีการ return นิพจน์ฟังก์ชั่นนั้นออกไป ลองดูตัวอย่างครับ
ภายในฟังก์ชั่น outer เรากำหนดฟังก์ชั่น inner ลงในตัวแปร f (กำหนดให้ตัวแปร f อ้างอิงไปยังฟังก์ชั่น inner) แล้ว return ค่าของตัวแปร f นี้ออกไป ดังนั้นการเรียกใช้ฟังก์ชั่น outer ในบรรทัดที่ 11 จึงได้ผลลัพธ์เป็นพอยเตอร์ที่อ้างอิงไปยังฟังก์ชั่น inner ซึ่งพอยเตอร์นี้ถูกเก็บลงตัวแปร g และทำให้ g() ในบรรทัดสุดท้ายเป็นการเรียกใช้ฟังก์ชั่น inner ที่อยู่ภายในฟังก์ชั่น outer นั่นเอง
คุณคงเดาได้ว่า ถ้ารันโค้ดข้างต้นจะปรากฏไดอะล็อกที่แสดงข้อความว่า “value of x is 123” ออกมา ใช่ครับ ได้ผลอย่างนั้นจริงๆ แต่คุณไม่สงสัยบ้างหรือว่า ตอนที่เราเรียกฟังก์ชั่น inner ผ่านตัวแปร g ในบรรทัดสุดท้ายนั้น ฟังก์ชั่น outer มันทำงานจบไปเรียบร้อยแล้ว แล้วตัวแปร x จะมีค่า 123 ได้อย่างไร เพราะ x เป็นตัวแปรโลคอลของฟังก์ชั่น outer ซึ่งควรจะต้องถูกทำลายไปเมื่อพ้นจากขอบเขต (Scope) ของ outer
ครับ สาเหตุที่ตัวแปร x ยังคงมีตัวตนอยู่ และยังเก็บค่า 123 ไว้ แม้ว่าฟังก์ชั่น outer จะจบการทำงานไปแล้ว ก็เพราะว่าตัวแปร x ถูกเก็บเอาไว้ใน Closure ครับ
ทีนี้ลองเพิ่มโค้ดอีกนิดหน่อย
เท่าที่อธิบายมาถึงตรงนี้ คุณคงเข้าใจแล้วว่า Closure คืออะไร แต่ยังไม่จบนะครับ ผมยังมีตัวอย่างเรื่อง Closure ที่น่าสนใจ (และน่าปวดหัว) อีกหลายตัวอย่างเลย รวมทั้งประโยชน์ของการใช้ Closure ด้วย แต่สำหรับบทความนี้คงเอาไว้แค่นี้ก่อน ถ้ามีคำแนะนำหรือข้อสงสัยใดๆก็ทิ้งคอมเมนต์ไว้ได้เลยครับ ^_^
http://promlert.com/2013/03/javascript_closure/
ConversionConversion EmoticonEmoticon