Rate this post

Trong JavaScript, this là một từ khóa đặc biệt và quan trọng, được sử dụng để tham chiếu đến đối tượng mà nó đang thuộc về. Tuy nhiên, giá trị của this không cố định mà phụ thuộc vào cách mà hàm chứa nó được gọi. Điều này có nghĩa là trong những ngữ cảnh khác nhau, this có thể tham chiếu đến các đối tượng khác nhau, tạo nên sự linh hoạt nhưng cũng gây ra không ít khó khăn trong việc hiểu và sử dụng chính xác.

Các trường hợp sử dụng this

Trong phương thức của một đối tượng

Khi this được sử dụng trong một phương thức của đối tượng, nó tham chiếu đến đối tượng chứa phương thức đó. Điều này có nghĩa là this cho phép bạn truy cập vào các thuộc tính và phương thức khác của cùng một đối tượng từ bên trong phương thức hiện tại.

Ví dụ minh họa:

const person = {
  name: 'John',
  greet: function() {
    console.log(`Hello, my name is ${this.name}`);
  }
};

person.greet(); // Hello, my name is John

Trong ví dụ trên, this.name trong phương thức greet tham chiếu đến thuộc tính name của đối tượng person.

Trong hàm tạo (constructor function)

Trong một hàm tạo (constructor function), this tham chiếu đến đối tượng mới được tạo ra khi bạn sử dụng từ khóa new. Điều này cho phép bạn thiết lập các thuộc tính và phương thức cho đối tượng mới.

Ví dụ minh họa:

function Person(name, age) {
  this.name = name;
  this.age = age;
}

const john = new Person('John', 30);
console.log(john.name); // John

Trong ví dụ này, this.namethis.age tham chiếu đến đối tượng john mới được tạo ra bởi hàm tạo Person.

Trong hàm thông thường

Sử dụng this trong một hàm thông thường sẽ phụ thuộc vào chế độ strict mode.

  • Trong strict mode: thisundefined, giúp tránh các lỗi tiềm ẩn khi bạn vô tình sử dụng this mà không có ngữ cảnh rõ ràng.
  • Ngoài strict mode: this tham chiếu đến đối tượng global, tức là window trong trình duyệt hoặc global trong Node.js.

Ví dụ minh họa:

function showThis() {
  console.log(this);
}

showThis(); // Trong strict mode: undefined, Ngoài strict mode: [object Window]

Ở đây, giá trị của this trong hàm showThis thay đổi tùy thuộc vào việc có sử dụng strict mode hay không.

Trong hàm callback

Giá trị của this trong một hàm callback phụ thuộc vào đối tượng gọi hàm callback đó. Điều này thường thấy trong các phương thức như setTimeout hoặc addEventListener, nơi mà this có thể thay đổi dựa trên ngữ cảnh gọi hàm.

Ví dụ minh họa với setTimeoutaddEventListener:

const button = {
  text: 'Click me',
  click: function() {
    setTimeout(function() {
      console.log(this.text);
    }, 1000);
  }
};

button.click(); // undefined vì this trong callback của setTimeout là window trong chế độ không strict.

document.getElementById('myButton').addEventListener('click', function() {
  console.log(this); // this ở đây tham chiếu đến phần tử DOM được nhấn.
});

Trong ví dụ đầu tiên với setTimeout, this không tham chiếu đến đối tượng button mà tham chiếu đến đối tượng global (window). Trong ví dụ thứ hai, this trong hàm callback của addEventListener tham chiếu đến phần tử DOM đã nhận sự kiện click.

Trong hàm arrow

Hàm arrow (=>) có một đặc điểm quan trọng: chúng không có this riêng của mình. Thay vào đó, this trong hàm arrow kế thừa từ phạm vi bên ngoài gần nhất nơi hàm arrow được định nghĩa.

Ví dụ minh họa:

const person = {
  name: 'Jane',
  greet: function() {
    setTimeout(() => {
      console.log(`Hello, my name is ${this.name}`);
    }, 1000);
  }
};

person.greet(); // Hello, my name is Jane

Trong ví dụ trên, this trong hàm arrow kế thừa từ greet và tham chiếu đến đối tượng person. Điều này giúp tránh được vấn đề thường gặp khi sử dụng this trong các callback của hàm thông thường.

Các giá trị của this trong JavaScript phụ thuộc rất nhiều vào ngữ cảnh gọi hàm, từ phương thức đối tượng, hàm tạo, hàm thông thường, hàm callback, đến hàm arrow. Hiểu rõ cách this hoạt động trong từng ngữ cảnh là chìa khóa để tránh các lỗi tiềm ẩn và sử dụng JavaScript một cách hiệu quả.

Thay đổi giá trị của this

Trong JavaScript, this thường được tự động thiết lập dựa trên ngữ cảnh gọi hàm. Tuy nhiên, có những trường hợp bạn cần kiểm soát và thay đổi giá trị của this theo cách thủ công. Ba phương thức phổ biến để thực hiện điều này là call(), apply(), và bind().

call()

Phương thức call() cho phép bạn gọi một hàm với một giá trị this cụ thể, và cũng có thể truyền thêm các đối số cho hàm đó. call() là một cách hữu hiệu để thay đổi ngữ cảnh của this trong các trường hợp mà bạn muốn áp dụng một phương thức của một đối tượng lên một đối tượng khác.

Ví dụ minh họa:

function greet() {
  console.log(`Hello, my name is ${this.name}`);
}

const person = { name: 'Alice' };
greet.call(person); // Hello, my name is Alice

Trong ví dụ này, greet.call(person) thay đổi giá trị của this trong hàm greet thành đối tượng person, cho phép hàm truy cập thuộc tính name của person.

apply()

Phương thức apply() tương tự như call(), nhưng có một điểm khác biệt: thay vì truyền các đối số riêng lẻ, bạn truyền chúng dưới dạng một mảng. Điều này đặc biệt hữu ích khi bạn có một danh sách các đối số mà bạn muốn truyền vào hàm mà không cần phải xác định từng đối số một.

Ví dụ minh họa:

function introduce(age, occupation) {
  console.log(`Hello, my name is ${this.name}, I'm ${age} years old and I work as a ${occupation}.`);
}

const person = { name: 'Bob' };
introduce.apply(person, [25, 'developer']); 
// Hello, my name is Bob, I'm 25 years old and I work as a developer.

Ở đây, apply() thay đổi giá trị của this thành đối tượng person, và truyền các đối số [25, 'developer'] vào hàm introduce.

bind()

Phương thức bind() khác biệt ở chỗ nó không gọi hàm ngay lập tức mà thay vào đó trả về một hàm mới với this được gán cố định theo giá trị bạn chỉ định. Điều này rất hữu ích khi bạn cần truyền một hàm như một callback với một this cụ thể.

Ví dụ minh họa:

function greet() {
  console.log(`Hello, my name is ${this.name}`);
}

const person = { name: 'Charlie' };
const greetPerson = greet.bind(person);

greetPerson(); // Hello, my name is Charlie

Trong ví dụ này, greet.bind(person) trả về một hàm mới greetPerson, với this được cố định là person. Bất kể greetPerson được gọi ở đâu, this luôn tham chiếu đến đối tượng person.

call(), apply(), và bind() là các phương thức mạnh mẽ cho phép bạn kiểm soát giá trị của this trong JavaScript. call()apply() thay đổi this và thực thi hàm ngay lập tức, trong khi bind() tạo ra một phiên bản mới của hàm với this cố định. Sử dụng những phương thức này một cách hợp lý giúp bạn linh hoạt hơn trong việc quản lý ngữ cảnh của this, đặc biệt trong các tình huống phức tạp như callback, sự kiện, và các hàm cao cấp.

Các trường hợp đặc biệt

Khi làm việc với các class trong JavaScript, từ khóa this có vai trò quan trọng trong việc tham chiếu đến các instance của class. Cụ thể, trong constructor của một class, this luôn tham chiếu đến đối tượng instance mới được tạo ra khi bạn sử dụng từ khóa new. Điều này cho phép bạn thiết lập các thuộc tính và khởi tạo giá trị cho đối tượng instance ngay khi nó được tạo ra.

Ví dụ minh họa trong constructor:

class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
}

const john = new Person('John', 30);
console.log(john.name); // John
console.log(john.age);  // 30

Trong ví dụ trên, this.namethis.age trong constructor tham chiếu đến instance của class Person, tức là đối tượng john, cho phép bạn thiết lập các thuộc tính nameage cho instance này.

Ngoài ra, this trong các phương thức khác của class cũng tham chiếu đến instance của class. Điều này cho phép các phương thức truy cập và thao tác với các thuộc tính của instance một cách trực tiếp.

Ví dụ minh họa trong phương thức khác:

class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }

  greet() {
    console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
  }
}

const jane = new Person('Jane', 25);
jane.greet(); // Hello, my name is Jane and I am 25 years old.

Trong ví dụ này, phương thức greet sử dụng this để tham chiếu đến instance jane của class Person, từ đó truy cập và hiển thị các thuộc tính nameage của đối tượng.

Như vậy, khi làm việc với class trong JavaScript, this trong constructor và các phương thức của class đều tham chiếu đến instance của class. Điều này giúp bạn dễ dàng quản lý và thao tác với các thuộc tính và phương thức của từng đối tượng cụ thể, đảm bảo tính linh hoạt và nhất quán trong mã nguồn. Sử dụng this đúng cách trong class không chỉ giúp tối ưu hóa hiệu suất mà còn làm cho mã nguồn dễ đọc và bảo trì hơn, góp phần quan trọng vào việc xây dựng các ứng dụng JavaScript mạnh mẽ và hiệu quả.

Mẹo và thủ thuật

Khi làm việc với this trong JavaScript, có một số mẹo và thủ thuật hữu ích mà bạn nên áp dụng để tránh các lỗi phổ biến và đảm bảo mã nguồn của bạn hoạt động chính xác. Đầu tiên, việc sử dụng strict mode (use strict) là một cách hiệu quả để tránh các lỗi liên quan đến this. Trong strict mode, this trong các hàm thông thường không tự động tham chiếu đến đối tượng global (như window trong trình duyệt) mà sẽ là undefined, giúp bạn tránh những sai sót khi vô tình sử dụng this ngoài ngữ cảnh mong đợi.

Mẹo 1: Sử dụng strict mode

'use strict';
function showThis() {
  console.log(this); // undefined
}
showThis();

Trong ví dụ trên, strict mode giúp ngăn this tham chiếu đến đối tượng global, giúp bạn dễ dàng phát hiện lỗi nếu sử dụng this không đúng cách.

Thứ hai, khi bạn cần giữ nguyên giá trị của this từ phạm vi bên ngoài, đặc biệt là trong các hàm callback hoặc trong các phương thức sử dụng trong class, hãy sử dụng hàm arrow. Hàm arrow không có this riêng mà kế thừa this từ phạm vi bên ngoài, giúp bạn duy trì đúng ngữ cảnh của this.

Mẹo 2: Sử dụng hàm arrow

class Timer {
  constructor() {
    this.seconds = 0;
  }

  start() {
    setInterval(() => {
      this.seconds++;
      console.log(this.seconds);
    }, 1000);
  }
}

const myTimer = new Timer();
myTimer.start();

Trong ví dụ này, hàm arrow trong setInterval kế thừa this từ class Timer, đảm bảo rằng this.seconds luôn tham chiếu đúng đến thuộc tính của instance myTimer.

Cuối cùng, khi bạn cần thay đổi giá trị của this, sử dụng các phương thức call(), apply(), hoặc bind(). Những phương thức này cho phép bạn kiểm soát this một cách linh hoạt, đặc biệt khi bạn muốn áp dụng một phương thức của đối tượng này lên một đối tượng khác hoặc cần gọi hàm với một ngữ cảnh this cụ thể.

Mẹo 3: Sử dụng call(), apply(), hoặc bind()

function greet() {
  console.log(`Hello, my name is ${this.name}`);
}

const person1 = { name: 'Alice' };
const person2 = { name: 'Bob' };

greet.call(person1); // Hello, my name is Alice
greet.apply(person2); // Hello, my name is Bob

const greetBob = greet.bind(person2);
greetBob(); // Hello, my name is Bob

Trong ví dụ này, call()apply() được sử dụng để thay đổi this thành các đối tượng person1person2. Phương thức bind() tạo ra một hàm mới với this cố định là person2, đảm bảo rằng khi hàm được gọi, this luôn đúng như mong đợi.

Bằng cách áp dụng các mẹo và thủ thuật này, bạn có thể tránh được các lỗi phổ biến liên quan đến this, đồng thời tối ưu hóa cách sử dụng this trong mã nguồn JavaScript của mình. Những kỹ thuật này không chỉ giúp mã của bạn trở nên rõ ràng và dễ hiểu hơn mà còn tăng tính linh hoạt và hiệu quả trong việc phát triển ứng dụng.

Tổng kết

Trong JavaScript, từ khóa this đóng vai trò quan trọng và linh hoạt, với giá trị của nó được xác định dựa trên ngữ cảnh mà nó được sử dụng. Các trường hợp sử dụng this bao gồm trong phương thức của một đối tượng, trong hàm tạo (constructor), trong các hàm thông thường (với sự khác biệt giữa strict mode và không strict mode), trong hàm callback, và trong hàm arrow. Mỗi trường hợp đều có cách xử lý riêng biệt, chẳng hạn như this trong phương thức của đối tượng sẽ tham chiếu đến đối tượng đó, trong khi this trong hàm arrow lại kế thừa từ phạm vi bên ngoài.

Việc hiểu rõ cách this hoạt động trong từng ngữ cảnh là vô cùng quan trọng để tránh các lỗi tiềm ẩn khi viết mã JavaScript. Sai sót trong việc sử dụng this có thể dẫn đến các lỗi khó phát hiện, như this không tham chiếu đúng đối tượng mong muốn hoặc this trở thành undefined trong strict mode. Bằng cách nắm vững các quy tắc và trường hợp sử dụng của this, bạn có thể viết mã JavaScript chính xác, hiệu quả và tránh được những vấn đề phổ biến, từ đó cải thiện chất lượng và độ tin cậy của ứng dụng.

Để lại một bình luận

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *

Contact Me on Zalo
Call now