this trong JavaScript là contextual keyword – giá trị được xác định bởi cách hàm được gọi (call-site), không phải nơi hàm được định nghĩa. Đây là khái niệm quan trọng và thường gây nhầm lẫn nhất.
4 Quy tắc xác định this (Priority Order)
1. New Binding (Ưu tiên cao nhất)
Gọi hàm với new → this = instance mới tạo.
function Person(name) {
this.name = name;
}
const an = new Person("An");
console.log(an.name); // "An"
2. Explicit Binding (call, apply, bind)
Gọi hàm với .call(), .apply(), .bind() → this = đối số đầu tiên.
function greet(greeting) {
console.log(`${greeting}, ${this.name}`);
}
const user = { name: "An" };
greet.call(user, "Xin chào"); // "Xin chào, An"
greet.apply(user, ["Chào"]); // "Chào, An"
const bound = greet.bind(user);
bound("Hello"); // "Hello, An"
3. Implicit Binding (Object method call)
Gọi hàm như method của object: obj.method() → this = obj.
const user = {
name: "An",
greet() { console.log(`Hi, ${this.name}`); }
};
user.greet(); // "Hi, An" - this = user
// ⚠️ Mất binding khi gán cho biến
const greetFn = user.greet;
greetFn(); // "Hi, undefined" (strict mode) hoặc "Hi, " (sloppy mode - this = window)
4. Default Binding (Mặc định)
Gọi hàm thường: func() → this = globalThis (window browser, global Node). Strict mode: this = undefined.
function foo() { console.log(this); }
foo(); // window (sloppy) / undefined (strict)
"use strict";
function bar() { console.log(this); }
bar(); // undefined
Arrow Function: Lexical this
Arrow function KHÔNG CÓ this riêng – nó kế thừa this từ scope bao quanh (lexical scoping).
const user = {
name: "An",
regular() { console.log(this.name); }, // this = user
arrow: () => console.log(this.name) // this = outer scope (window/module)
};
user.regular(); // "An"
user.arrow(); // undefined (hoặc window.name)
Ví dụ từ bài gốc
var college = {
subject: "Maths",
lecturer: "Amy",
details: function() {
document.write(this.subject); // this = college
}
};
college.details(); // "Maths"
Các tình huống dễ bị lỗi (Gotchas)
| Tình huống | Vấn đề | Giải pháp |
|---|---|---|
| Callback / Event handler | this = element (event) hoặc undefined | Arrow function, bind, hoặc e.currentTarget |
| Method gán cho biến | Mất binding (this = global/undefined) | bind() hoặc arrow function property |
| Nested function | Inner function this = global/undefined | Arrow function, const self = this, bind |
| setTimeout/Interval | this = global/undefined | Arrow function hoặc bind |
Best Practice Hiện Đại
- Dùng Arrow function cho callback, method không cần dynamic this.
- Dùng Method shorthand trong object/class:
method() {}. - Cần dynamic this → dùng
function() {}+.bind(this)hoặccall/apply. - Luôn bật Strict Mode (
"use strict"hoặc ES Modules) –this=undefinedthay vìwindowgiúp bắt bug sớm. - Class fields (ES2022):
method = () => {}– auto-bind this.
// Class field arrow function - auto bind
class Component {
state = { count: 0 };
handleClick = () => {
console.log(this.state.count); // this luôn = instance
};
render() {
return ;
}
}