Những điều cần biết về “this” trong JavaScript

Bạn đang thấy khó hiểu với từ khóa “this” trong JavaScript? Vậy thì mời bạn đọc những điều cần biết về “this” trong JavaScript dưới đây.

Lập trình JavaScript

“this” trong global scope

Trong phạm vi này, this sẽ trả về đối tượng window miễn là nó nằm ngoài một hàm. Global context có nghĩa bạn không đặt nó vào bên trong một hàm.

if(true) {
  console.log(this) // returns window object
}

let i = 2
while(i < 10) {
  console.log(this) // returns window object till i === 9
  i++
}

Nếu chạy code trên, bạn sẽ có đối tượng window.

“this” bên trong hàm

Khi được dùng trong hàm, this chỉ đối tượng mà hàm được liên kết. Ngoại lệ ở đây là khi bạn dùng this trong một hàm độc lập, mà nó trả về đối tượng window. Hãy cùng xem xét một số ví dụ.

Ở ví dụ sau, hàm sayName nằm bên trong đối tượng me. Ở đây, this tham chiếu tới đối tượng chứa hàm này.

 
function sayName() {
  return `My name is ${this.name}`
}

const me = {
  name: "Kingsley",
  sayName: sayName
}

console.log(me.sayName()) // My name is Kingsley

This là đối tượng me, vì thế, nói this.name bên trong phương thức sayName hoàn toàn giống me.name.

Một cách khác để định nghĩa về nó là bất cứ thứ gì ở phía bên trái của hàm khi được gọi sẽ là this. Điều này có nghĩa bạn có thể sử dụng lại hàm sayName trong các đối tượng khác nhau và điều này sẽ đề cập đến một bối cảnh hoàn toàn khác nhau mỗi lần.

Giờ như đã nói từ đầu, this trả về đối tượng window khi được dùng trong một hàm độc lập. Điều này do một hàm độc lập mặc định được liên kết với đối tượng window.

function talk() {
  return this
}

talk() // returns the window object

Gọi talk() giống như gọi window.talk(). Bất cứ thứ gì ở bên trái của hàm đều sẽ tự động trở thành this.

Ngoài ra, từ khóa this trong hàm này hoạt động khác ở chế độ nghiêm ngặt của JavaScript. Điều này cũng cần lưu ý khi bạn đang dùng thư viện UI sử dụng strict mode.

Dùng “this” với Function.bind()

Có những trường hợp bạn không thể thêm hàm vào một đối tượng dưới dạng phương thức.

Có lẽ đối tượng đó không phải của bạn và bạn sẽ lấy nó ra từ một thư viện. Đối tượng này có thể đột biến, vì thế, bạn không thể thay đổi nó. Trong những trường hợp như thế, bạn vẫn có thể chạy riêng từng hàm từ đối tượng này bằng phương thức Function.bind().

Ở ví dụ sau, hàm sayName không phải phương thức trên đối tượng me, nhưng bạn vẫn ràng buộc nó bằng hàm bind():

function sayName() {
  return `My name is ${this.name}`
}

const me = {
  name: "Kingsley"
}

const meTalk = sayName.bind(me)

meTalk() // My name is Kingsley

Bất cứ đối tượng bạn chuyển vào bind() sẽ được dùng làm giá trị của this trong lệnh gọi hàm đó.

Tóm lại, bạn có thể dùng bind() trên hàm bất kỳ và chuyển nó vào trong một ngữ cảnh mới (một đối tượng). Đối tượng đó sẽ ghi đè lên ý nghĩa của this bên trong hàm đó.

Dùng “this” với function.call()

Điều gì sẽ xảy ra nếu bạn không muốn trả về toàn bộ hàm mới, mà chỉ gọi hàm này sau khi liên kết nó với ngữ cảnh? Giải pháp ở đây là dùng phương thức call():

function sayName() {
  return `My name is ${this.name}`
}

const me = {
  name: "Kingsley"
}

sayName.call(me) // My name is Kingsley

Phương thức call() ngay lập tức chạy hàm này thay vì trả về một hàm khác.

Nếu hàm này cần một tham số, bạn có thể chuyển nó qua phương thức call(). Ở ví dụ sau, bạn đang chuyển ngôn ngữ sang hàm sayName(), vì thế, bạn có thể dùng nó để trả về các thông báo khác nhau theo điều kiện:

function sayName(lang) {
  if (lang === "en") {
    return `My name is ${this.name}`
  } else if (lang === "it") {
    return `Io sono ${this.name}`
  }
}

const me = {
  name: "Kingsley"
}

sayName.call(me, 'en') // My name is Kingsley
sayName.call(me, 'it') // Io sono Kingsley

Như bạn có thể thấy, bạn có thể chuyển bất kỳ tham số mong muốn sang hàm này làm đối số thứ hai cho phương thức call(). Bạn cũng có thể chuyển bao nhiêu tham số tùy thích.

Phương thức apply() tương tự như call()bind(). Điểm khác biệt duy nhất ở đây là bạn chuyển nhiều đối số bằng cách phân tách chúng bằng dấu phẩy qua call(), trong khi chuyển nhiều đối số trong một mảng bằng apply().

Tóm lại, bind(), call(), and apply() đều cho phép bạn gọi hàm bằng một đối tượng hoàn toàn khác mà không có bất kỳ mối quan hệ nào giữa hai đối tượng đó.

“this” trong hàm constructor

Nếu bạn gọi một hàm bằng từ khóa key, nó tạo một đối tượng this và trả về nó:

function person(name){
  this.name = name
}

const me = new person("Kingsley")
const her = new person("Sarah")
const him = new person("Jake")

me.name // Kingsley
her.name // Sarah
him.name // Jake

Ở code trên, bạn đã tạo 3 đối tượng khác nhau từ cùng một hàm. Từ khóa new tự động tạo một liên kết giữa đối tượng đang được tạo và từ khóa this bên trong hàm này.

“this” trong hàm callback

Hàm callback khác những hàm bình thường khác. Chúng là các hàm mà bạn chuyển sang hàm khác dưới dạng đối số, vì thế, chúng có thể được thực thi ngay lập tức sau khi hàm chính triển khai xong.

Từ khóa this tham chiếu tới một ngữ cảnh hoàn toàn khác khi được dùng trong hàm callback:

function person(name){
  this.name = name
  setTimeout(function() {
    console.log(this)
  }, 1000)
}

const me = new person("Kingsley") // returns the window object

Sau một giây gọi hàm constructor person và tạo đối tượng me mới, nó sẽ ghi lại đối tượng cửa sổ làm giá trị của this. Vì thế, khi được dùng trong một hàm callback, this tham chiếu tới đối tượng cửa sổ và không phải đối tượng “constructed”.

Bạn có hai cách để khắc phục vấn đề trên. Phương thức đầu tiên là dùng bind() để liên kết hàm person với đối tượng được xây dựng mới:

function person(name){
  this.name = name
  setTimeout(function() {
    console.log(this)
  }.bind(this), 1000)
}

const me = new person("Kingsley") // returns the me object

Với chỉnh sửa trên, this trong callback sẽ trỏ tới this giống như hàm khởi tạo.

Cách thứ hai để giải quyết vấn đề của this trong hàm callback là sử dụng các hàm mũi tên.

“this” bên trong các hàm mũi tên

Hàm mũi tên khác với những hàm thông thường. Bạn có thể biến hàm callback thành hàm mũi tên. Với hàm này, bạn không còn cần bind() vì nó tự động liên kết với đối tượng được xây dựng mới:

function person(name){
  this.name = name
  setTimeout(() => {
    console.log(this)
  }, 1000)
}

const me = new person("Kingsley") // returns the me object

Trên đây là những điều bạn cần biết về từ khóa “this” và ý nghĩa của nó ở tất cả ngữ cảnh khác nhau trong JavaScript. Nếu mới học lập trình JavaScript, kiến thức trên rất hữu ích với bạn.

Thứ Sáu, 25/08/2023 16:47
51 👨 111
0 Bình luận
Sắp xếp theo