JavaScript
Object
bind
เรียนรู้ .bind() — explicit binding ที่สร้างฟังก์ชันใหม่โดยตรึง this ไว้ล่วงหน้า ไม่ต้องเรียกทันที ใช้แก้ปัญหา this หายใน callback ได้ตรงจุดและเป็นวิธีที่พบบ่อยในโค้ดจริง
.bind() ต่างจาก .call() และ .apply() อย่างไร
บทที่แล้วเราเรียน `.call()` และ `.apply()` — ทั้งคู่ **เรียกฟังก์ชันทันที** พร้อมกำหนด `this` `.bind()` ทำงานต่างออกไปโดยสิ้นเชิง: - **ไม่เรียกฟังก์ชันทันที** — แต่คืน **ฟังก์ชันใหม่** ที่ `this` ถูกตรึง (bind) ไว้แล้ว - ฟังก์ชันใหม่นี้จะใช้ `this` ตามที่เรากำหนดเสมอ — ไม่ว่าใครเป็นคนเรียก เปรียบเทียบ: `.call()` กับ `.apply()` ใช้เมื่ออยากเรียกเดี๋ยวนี้ — `.bind()` ใช้เมื่ออยากตรึง `this` ไว้ก่อน แล้วเรียกทีหลัง (เช่น ส่งเป็น callback)
| .call() / .apply() | .bind() | |
|---|---|---|
| เรียกฟังก์ชันทันที? | ✓ | ✗ — คืนฟังก์ชันใหม่ |
| กำหนด this ได้? | ✓ | ✓ — ถาวร |
| ใช้กับ callback ได้? | ไม่สะดวก — เรียกทันที | ใช่ — นี่คือ use case หลัก |
| this เปลี่ยนตาม caller ได้? | หลัง call — ไม่ (เรียกครั้งเดียว) | ไม่ — this ถูกตรึงถาวร |
fn.bind(thisArg) — ตรึง this ไว้ ไม่ต้องเรียกทันที
รูปแบบ: `const boundFn = fn.bind(thisArg)` `boundFn` เป็นฟังก์ชันใหม่ที่มีพฤติกรรมเหมือน `fn` ทุกประการ — ต่างแค่ว่า `this` ถูกตรึงเป็น `thisArg` ตลอดกาล ไม่ว่าเราจะเรียก `boundFn` แบบ standalone (`boundFn()`) หรือส่งเป็น callback (`setTimeout(boundFn, 100)`) — `this` ใน `boundFn` จะเป็น `thisArg` เสมอ
const person = {
name: "Alice",
greet: function () {
return "สวัสดี จาก " + this.name;
},
};
// ถอด method → this หาย (default binding)
const unbound = person.greet;
console.log(unbound()); // "สวัสดี จาก undefined"
// bind → ตรึง this = person → ปลอดภัย
const bound = person.greet.bind(person);
console.log(bound()); // "สวัสดี จาก Alice" ✓
// bound เรียกจากที่ไหนก็ได้ this ก็ยังเป็น person
setTimeout(function () {
console.log(bound()); // "สวัสดี จาก Alice" ✓
}, 100);แก้ปัญหา this หายใน callback ด้วย .bind()
จากบท `this` เราเห็นว่า `setTimeout(obj.method, 100)` ทำให้ `this` หาย — เพราะ callback ถูกเรียกแบบ standalone (default binding) เราเรียนวิธีแก้ด้วย arrow function ไปแล้ว (`setTimeout(() => { ... })`) อีกวิธีที่ใช้บ่อยในโค้ดจริงคือ `.bind()` — ใช้ inline ตรงจุดที่ส่ง callback เลย: `setTimeout(obj.method.bind(obj), 100)` ข้อดีของ `.bind()`: ใช้แก้ `this` ใน callback โดยไม่ต้องแก้ไขฟังก์ชันเดิมเลย — แค่ `.bind(obj)` ต่อท้าย
`.bind(this)` ที่ต่อท้าย regular function callback — ตรึง `this` = `timer` ทำให้ `this.message` ได้ "หมดเวลา!"
const timer = {
message: "หมดเวลา!",
seconds: 3,
start: function () {
// ❌ แบบผิด — callback() → this = window
// setTimeout(function () {
// console.log(this.message);
// }, 3000);
// ✅ แบบถูก — ใช้ .bind(this)
setTimeout(function () {
console.log(this.message); // "หมดเวลา!"
}.bind(this), 3000);
// ^^^^^^^^^ — ตรึง this = timer
// ✅ อีกแบบ — arrow function (เรียนไปแล้ว)
// setTimeout(() => {
// console.log(this.message); // "หมดเวลา!"
// }, 3000);
},
};
timer.start(); // อีก 3 วินาที: "หมดเวลา!"// .bind() กับ setInterval — ใช้ได้เหมือนกัน
const counter = {
count: 0,
start: function () {
this.id = setInterval(function () {
this.count++;
console.log("Count:", this.count);
}.bind(this), 1000);
// ^^^^^^^^^ — this = counter ตลอดกาล
},
stop: function () {
clearInterval(this.id);
console.log("หยุดที่ Count:", this.count);
},
};
counter.start();
setTimeout(function () {
counter.stop(); // หยุดหลังจาก 5 วินาที
}.bind(counter), 5000);
// ^^^^^^^^^^^^ — แม้ setTimeout callback เองก็ใช้ bind ได้เปรียบเทียบ 3 วิธีแก้ this หายใน callback
ตอนนี้เรามี 3 วิธีหลักในการรักษา `this` ใน callback — แต่ละวิธีเหมาะกับสถานการณ์ต่างกัน
| วิธี | Syntax | ข้อดี | ข้อเสีย |
|---|---|---|---|
| Arrow function | `setTimeout(() => { ... }, ms)` | สั้น อ่านง่าย เป็นที่นิยม | ใช้เป็น method ของ object ไม่ได้ |
| `.bind(this)` | `setTimeout(function() { ... }.bind(this), ms)` | ใช้ regular function ได้ — แยกส่วนฟังก์ชันกับ context ชัดเจน | ยาวกว่า arrow นิดหน่อย |
| `.call()` / `.apply()` | `fn.call(obj, args)` | กำหนด this ได้ชั่วคราว ไม่ต้องสร้างฟังก์ชันใหม่ | ไม่เหมาะกับ callback — เรียกฟังก์ชันทันที |
- **Arrow function** → ทางเลือกแรกสำหรับ callback ใน method — สั้น สะอาด อ่านง่าย
- **`.bind(this)`** → เมื่อต้องการใช้ regular function หรือเมื่อแยกฟังก์ชันไว้ข้างนอก method — `.bind()` ต่อท้ายตอนส่งเป็น callback
- **`.call()` / `.apply()`** → เมื่อต้องการยืม method ชั่วคราว หรือเรียกฟังก์ชันทันทีด้วย this ที่ต่างออกไป
Use case จริง — ส่ง method เป็น callback ให้ event listener
อีกสถานการณ์ที่ `this` หายบ่อยคือการส่ง method เป็น event callback — เช่น `element.addEventListener('click', obj.handleClick)` นี่เป็น use case คลาสสิกของ `.bind()` — เพราะเราไม่สามารถควบคุมว่า browser จะเรียก callback อย่างไร (browser เรียกแบบ standalone → `this` = element)
`simulateEvent(boundHandler)` — boundHandler มี this ตรึงเป็น button → this.label ยังคงเป็น "ส่งฟอร์ม"
// จำลอง event — method ถูกเรียกแบบ standalone
const button = {
label: "ส่งฟอร์ม",
handleClick: function () {
console.log("กดปุ่ม:", this.label);
},
};
// ❌ ส่ง method ตรง ๆ — this จะเป็น caller (เช่น button element)
// button.handleClick() → this = button ✓
// แต่ถ้าเป็น event callback → this = element ไม่ใช่ button
// ✅ ใช้ .bind() ตรึง this ไว้
const boundHandler = button.handleClick.bind(button);
// ต่อให้ boundHandler ถูกเรียกแบบ standalone
// this ก็ยังเป็น button
boundHandler(); // "กดปุ่ม: ส่งฟอร์ม" ✓
// จำลอง: สมมติ function ภายนอกเรียก callback
function simulateEvent(callback) {
callback(); // ← standalone call
}
simulateEvent(boundHandler); // "กดปุ่ม: ส่งฟอร์ม" ✓
simulateEvent(button.handleClick); // "กดปุ่ม: undefined" ✗ข้อควรรู้ — bind ใช้ครั้งเดียว ตรึงถาวร
ฟังก์ชันที่ถูก `.bind()` แล้ว **ไม่สามารถ bind ซ้ำได้** — `this` ที่ถูกตรึงไว้จากการ bind ครั้งแรกจะอยู่ถาวร และ `.bind()` สร้างฟังก์ชันใหม่ทุกครั้งที่เรียก — ไม่ได้แก้ไขฟังก์ชันเดิม
function showThis() {
console.log(this.name);
}
const obj1 = { name: "Object 1" };
const obj2 = { name: "Object 2" };
// bind ครั้งแรก — this = obj1
const boundToObj1 = showThis.bind(obj1);
boundToObj1(); // "Object 1"
// พยายาม bind ซ้ำ — ไม่มีผล!
const boundAgain = boundToObj1.bind(obj2);
boundAgain(); // "Object 1" — this ยังเป็น obj1!
// bind สร้างฟังก์ชันใหม่ — ฟังก์ชันเดิมไม่เปลี่ยน
showThis.call(obj2); // "Object 2" — showThis ยังใช้ call ได้ปกติplayground: ลองใช้ .bind() แก้ this หาย
สรุป
.bind() สร้างฟังก์ชันใหม่ที่ this ถูกตรึงไว้ถาวร — เรียกจากที่ไหนก็ได้ this ไม่เปลี่ยน
- **`.bind(thisArg)`** — ไม่เรียกฟังก์ชันทันที แต่คืนฟังก์ชันใหม่ที่ `this` ถูกตรึงถาวร
- **`.bind()` ใช้คู่กับ callback** — `setTimeout(fn.bind(this), ms)` หรือ `setInterval(fn.bind(this), ms)`
- **`.bind()` สร้างฟังก์ชันใหม่ทุกครั้ง** — ฟังก์ชันเดิมไม่เปลี่ยน; bind ซ้ำไม่มีผล
- **3 วิธีแก้ `this` หายใน callback:** arrow function (สั้น อ่านง่าย), `.bind(this)` (ใช้ regular function ได้), `.call()` (ใช้ยืม method ชั่วคราว)
- **Explicit binding** (`call`, `apply`, `bind`) = เราควบคุม `this` เอง — ไม่ต้องขึ้นอยู่กับ caller — นี่คือพลังของการเข้าใจ `this` อย่างแท้จริง