คำว่า this ใน JavaScript มักจะสร้างความสับสนเสมอ และเป็นหนึ่งในกับดักที่พบบ่อยที่สุดใน JavaScript ซึ่งไม่ใช่การออกแบบที่ดีใน JavaScript (คุณสามารถอ้างอิงข้อบกพร่องในการออกแบบอื่นๆ ของ JavaScript ได้ ที่นี่) เนื่องจากคุณสมบัติ lazy binding ทำให้มันสามารถเป็น global object, current object หรือ... บางคนถึงกับหลีกเลี่ยงการใช้ this ใน JavaScript
จริงๆ แล้ว ถ้าคุณเข้าใจวิธีการทำงานของ this อย่างถ่องแท้ คุณจะรู้วิธีหลีกเลี่ยงกับดักเหล่านี้ มาดูกันว่า this ชี้ไปที่อะไรในสถานการณ์ต่อไปนี้
1. ในโค้ด global scope
alert(this)//window
this จะชี้ไปที่ global object (โดยปกติคือ window ใน web browsers) ในโค้ด global scope
2. ในการเรียกฟังก์ชันแบบ pure
function fooCoder(x) { this.x = x; } fooCoder(2); alert(x);// Global variable x's value is 2
ในที่นี้ this ก็ชี้ไปที่ global object เช่นกัน เนื่องจากฟังก์ชันที่กำหนดไว้ใน global scope จริงๆ แล้วเป็น method หนึ่งของ global object ดังนั้น this จะเป็น global object ใน strict mode, this จะเป็น undefined
3. ในการเรียก method ของ object
var name = "clever coder"; var person = { name : "foocoder", hello : function(sth){ console.log(this.name + " says " + sth); } } person.hello("hello world");
ผลลัพธ์จะเป็น "foocoder says hello world" this จะชี้ไปที่ person object นั่นคือ current object ที่เรียก method
4. ใน constructor
new FooCoder();
ใน constructor this จะชี้ไปที่ object ที่สร้างขึ้นใหม่ด้วย new
5. ในการเรียกฟังก์ชัน private
var name = "clever coder"; var person = { name : "foocoder", hello : function(sth){ var sayhello = function(sth) { console.log(this.name + " says " + sth); }; sayhello(sth); } } person.hello("hello world");//clever coder says hello world
ในฟังก์ชัน private, this ไม่ได้ binding กับ object ของ method ภายนอก แต่ binding กับ global object แทน ซึ่งถือเป็นข้อบกพร่องในการออกแบบของ JavaScript เพราะไม่มีใครต้องการให้ this ในฟังก์ชัน private ชี้ไปที่ global object ในที่นี้ วิธีแก้ปัญหาทั่วไปคือการกำหนด this ให้กับตัวแปรอื่นและอ้างอิงตัวแปรนั้นในฟังก์ชัน private
var name = "clever coder"; var person = { name : "foocoder", hello : function(sth){ var that = this; var sayhello = function(sth) { console.log(that.name + " says " + sth); }; sayhello(sth); } } person.hello("hello world");//foocoder says hello world
6. ใน call() หรือ apply()
person.hello.call(person, "world");
apply() และ call() คล้ายกัน ความแตกต่างเพียงอย่างเดียวคือ parameters ที่ส่งเข้ามาหลังจาก parameter แรก ใน apply(), parameters อื่นๆ จะถูกส่งด้วย array ในขณะที่ใน call(), parameters อื่นๆ จะถูกส่งแยกกัน
call( thisArg [,arg1,arg2,… ] ); // Parameter list,arg1,arg2,... apply(thisArg [,argArray] ); // Parameter list,argArray
parameter แรกที่ส่งเข้ามาคือ object ที่ this จะชี้ไป เราสามารถระบุ object ใดๆ ที่จะให้ this ชี้ไปได้
7. อื่นๆ
เราอาจเห็นโค้ดด้านล่างนี้บ่อยๆ:
$("#some-ele").click = obj.handler;
ถ้าเราใช้ this ใน handler, this จะ bind กับ obj หรือไม่? เห็นได้ชัดว่าไม่ หลังจาก assignment, ฟังก์ชันจะถูกเรียกใน callback function, this จะ bind กับ $("#some-div") element นี่คือสิ่งที่เราต้องเข้าใจในที่นี้ -- execution context
แล้วเราจะระบุ this object ให้เป็นสิ่งที่เราต้องการใน callback function ได้อย่างไร? ใน ECMAScript 5 มี method bind():
fun.bind(thisArg[, arg1[, arg2[, ...]]])
thisArg จะเป็น this object ที่เราต้องการให้เป็น
$("#some-ele").click(person.hello.bind(person));
ตอนนี้ this จะเป็น person object
ใน Prototype.js เราสามารถค้นหา implementation ของ bind() ได้:
Function.prototype.bind = function(){ var fn = this, args = Array.prototype.slice.call(arguments), object = args.shift(); return function(){ return fn.apply(object, args.concat(Array.prototype.slice.call(arguments))); }; };
สรุป
1. เมื่อฟังก์ชันถูกเรียกเป็น method ของ object, this จะชี้ไปที่ object นั้น
2. เมื่ออยู่ใน pure function call, this จะเป็น global object (ใน strict mode, this จะเป็น undefined)
3. ใน constructor, this จะเป็น object ที่สร้างขึ้นใหม่
ในประโยคเดียว this จะชี้ไปที่ object ที่ฟังก์ชันถูกเรียกเสมอ