Javascript: Bind, Call, Apply กับ function

ช่วงนี้มีโปรเจคที่ต้องเขียน Javascript อย่างหนักหน่วง ก็เลยมีเวลาได้อ่านเรื่อง bind, call, apply ซึ่งเป็นวิธีจัดการกับ Function ที่ยืดหยุ่นกว่าสิ่งที่เคยได้ร่ำเรียนมาในระดับมหาวิทยาลัย เพื่อไม่ให้ลืม (เพราะเอาเข้าจริง ๆ ถ้าไม่ได้เขียนบ่อย ๆ ก็คงลืม) ก็เลยขอสรุปย่อ ๆ ไว้ ณ ที่นี้

bind, call, และ apply เป็น method พิเศษที่มากับ function ในภาษา Javascript (call และ apply มาในช่วง ECMAScript 3 แถว ๆ IE 6 ส่วน bind มาใน ECMAScript 5 แถว ๆ IE 10) วิธีประกาศ function ในภาษา Javascript นั้นสามารถทำได้ 2 แบบคือ

function hello(a, b, c) { }

หรือ

var hello = function (a, b, c) { }

ฟังก์ขั่น hello ตามตัวอย่างนี้มี argument 3 ตัวคือ a, b, c แต่จริง ๆ แล้วภายในฟังก์ชั่นนี้ (หรือที่อื่น ๆ) ยังมีตัวแปรลับอีกตัวคือ this ซึ่งค่าจะขึ้นกับบริบทที่มันถูกใช้งาน เช่น หากมันถูกเรียกจาก event ต่าง ๆ มันอาจจะกลายเป็น HTMLElement ของ event นั้นหรือถ้าเรียกลอย ๆ เลยก็จะเป็น window นั่นก็นำมาสู่ปัญหาเมื่อมีการอ้างถึง this ภายในฟังก์ชั่นว่า ตกลงว่า this นี่คืออะไรกันแน่? bind, call, และ apply เกิดมาเพื่อบังคับให้ this เป็นค่าที่เราต้องการได้

call และ apply เรียกฟังก์ชั่นโดยการกำหนดค่า this

call และ apply นั้น เป็นวิธีเรียกใช้งานฟังก์ชั่น โดยกำหนดค่า this เมื่อต้องใช้งาน แต่สิ่งที่แตกต่างคือ call นั้นรับค่าเป็นตัวแปรธรรมดา แต่ apply รับค่าเป็น array

var r1 = hello.call("5", "a", "b", "c");
var r2 = hello.apply("5", ["a", "b", "c"]);

จากตัวอย่างนี้ ทั้ง call และ apply เป็นการเรียก hello ที่ค่าของ this เป็น "5" แต่ที่ต่างคือ ท่อนหลังของ call เป็นการส่งค่าให้ a, b, c เป็น "a", "b", "c" ตามลำดับ แต่ส่วนของ apply นั้นส่ง array ไปแทนและค่าจะถูกแตกให้กับ a, b, c เป็น "a", "b", "c" ตามลำดับ

bind ผูก argument ให้กับฟังก์ชั่นเดิม กลายเป็นฟังก์ชั่นใหม่

bind เป็น method พิเศษที่มากับฟังก์ชั่น (IE < 9, Firefox < 3 ใช้ไม่ได้) จุดประสงค์ของมันในตอนแรก ใช้เพื่อห่อฟังก์ชั่นเดิม ให้กลายเป็นฟังก์ชั่นใหม่ที่ถูกตั้งค่า argument บางอย่างไว้ (ต่างกับ apply และ call ที่เป็นการเรียกใช้ฟังก์ชั่นตรง ๆ)

การเรียก bind กับ hello นี้จะทำให้เกิดฟังก์ชั่นใหม่ เช่น

var hello2 = hello.bind("5");
hello2("a", "b", "c");

จากตัวอย่างนี้ เราจะได้ฟังก์ชั่น hello2 ซึ่งเมื่อเรียกใช้งานจะเสมือนการเรียก hello เพียงแต่ว่าหากใน hello มีการกล่าวถึง this ค่าของ this จะมีค่า "5" ค่า a, b, c เป็นไปตามลำดับที่เรียก นั่นคือ "a", "b", "c"

var hello3 = hello.bind("5", "z");
var r3 = hello3("b", "c");

จากตัวอย่างนี้ เราจะได้ฟังก์ชั่น hello3 ซึ่งเมื่อเรียกใช้งานจะเสมือนการเรียก hello เพียงแต่คราวนี้ ทั้ง this และ a จะถูกกำหนดค่าเป็น "5" และ "z" ตามลำดับ ส่วนค่าของ b, c เป็นไปตามลำดับที่เรียก นั่นคือ "b", "c" ผลลัพธ์ที่ได้ (ถ้ามี return ใน hello) ก็คือ r3

ตัวอย่างการใช้งานอื่น ๆ เช่น จะผูก hello เข้ากับการกดปุ่ม (สมมติว่ามี id เป็น test) แต่ต้องการบังคับ this และ argument อื่น ก็สามารถทำได้ดังนี้

document.querySelector('#test').addEventListener('click', hello.bind('5', 'a', 'b', 'c'));

แล้วจะเขียนให้ยากทำไมล่ะ?

บางครั้งการที่นำ code มาจากแหล่งอื่นแล้วในนั้นมีการอ้างถึง this (ซึ่งหากเล่นกันถึงขนาดนี้แล้ว code มันคงไม่ใช่ hello world ง่าย ๆ แน่ น่าจะมีการเขียนเป็น Javascript Object) หากเรานำโค้ดไปใช้เลยก็อาจจะทำให้มันทำงานไม่เหมือนแหล่งต้นทางที่ไปเอา code มาก็ได้ เราจึงต้องการกำหนด this ซึ่งการเรียกใช้งาน hello ตามปรกติ จะไม่สามารถทำสิ่งนี้ได้ แต่ทั้งนี้ก็ขึ้นกับนักพัฒนาว่าจะนำเอารูปแบบการเขียนโค้ดนี้ไปใช้ประยุกต์กับอะไร

Blog Tags: 

Add new comment