+2

This trong JS

Ta có thể hiểu this trong JS cũng giống như trong ngôn ngữ tự nhiên của tiếng Anh. Nó giống như một danh từ để thay thế cho danh từ khác tránh việc lặp đi lặp lại. Ví dụ

Michael is handsome because he is rich

danh từ he ở trên là để tránh việc lặp lại Michael

Xét tiếp ví dụ tiếp về sử dụng this trong JS

var person = {
firstName: "Michael",
lastName: "Scofield",
  fullName: function () {
      console.log(this.firstName + " " + this.lastName);
      // ta cũng có thể viết theo cách này
      console.log(person.firstName + " " + person.lastName);
  }
}
person.fullName()
 Michael Scofield
 Michael Scofield
  • Như ví dụ trên có thể thấy việc dùng this hay chính object đó để gọi là tương đương đều in ra 2 dòng fullName như nhau Nhưng khi dùng object person để gọi thì có vấn đề khác đó là có thể có một global variable nào đó bên ngoài cũng có tên person và khi gọi person có thể nó sẽ không lấy giá trị từ bên trong object mà bạn muốn.
  • Điều đó dễ gây ra bug cũng như Việc debug sẽ trở nên khó khăn hơn.

Javascipt this

Tất cả các function trong JS đều có các properties, nó cũng giống như các object. Khi thực thi function nó sẽ lấy this property của object

this luôn luôn refer và giữ giá trị của object. Nó thường được sử dụng trong function hoặc method.

this được sử dụng bên trong function (giả sử là function A), nó chứa giá trị của object mà gọi đến function A

Chúng ta cần this để accesss vào các method và thuộc tính của object mà nó gọi đến function A, đặc biệt alf khi chúng ta không biết tên của object được gọi và đôi khi là một một object không có tên. this là shortcut để refer tới invoking object.

$ (".button").click (function (event) {
   console.log ($ (this).prop ("name"));
});

$(this) Là jQuery syntax nó tương tự như this trong JavaScript, được sử dụng trong anonymous function. Anonymous function này sẽ thực thi phương thức click và lý do mà $this được bind tới button object là vì jQuery library bind $(this) tới object mà nó gọi phương thức click do đó $(this) sẽ có giá trị của $(".button") object.

this không được gán giá trị cho tới khi một object gọi function mà this được định nghĩa trong function này

Sử dụng this ở global scope

  • Ở global scope khi code được thực thi ở browser, tất cả các global variable và các function được định nghĩa bên trong window object. Do đó, khi chúng ta sử this ở global function nó sẽ refer, và lấy giá trị của global window object.
var firstName = "Peter",
 lastName = "Ally";
​
 function showFullName () {
 // "this" sẽ chứa giá trị của window object bởi vì fullName được định nghĩa  ở global scope, giống như firstName, lastName
  console.log (this.firstName + " " + this.lastName);
 }
​
 var person = {
   firstName   :"Penelope",
   lastName    :"Barrymore",
   showFullName:function () {
   // `this` trong trường hợp này tham chiếu tới person object, bởi vì hàm showFullName này sẽ được gọi bởi person object
   console.log (this.firstName + " " + this.lastName);
   }
 }
​
 showFullName (); // Peter Ally​
​
 // window là một object mà nó có thể gọi được tất cả các biến hay các function được định nghĩa ở global
 window.showFullName (); // Peter Ally​
​
 // this ở bên trong showFullName method được định nghĩa bên trong person object do đó khi gọi nói sẽ lấy object là person
 person.showFullName (); // Penelope Barrymore

Khi this được sử dụng bên trong method, được truyền qua callback function

  var user = {
    data:[
      {name:"T. Woods", age:37},
      {name:"P. Mickelson", age:43}
    ],
    clickHandler:function (event) {
      var randomNum = ((Math.random () * 2 | 0) + 1) - 1; // lấy random 0 hoặc 1
      console.log (this.data[randomNum].name + " " + this.data[randomNum].age);
      }
  }
  ​
    // Outut là undefined bởi vì không button jQuery không có data property nào cả
  $ ("button").click (user.clickHandler); // Cannot read property '0' of undefined

Đoạn code ở trên chúng ta đang truyền user.clickHandler tới click function của jQuery object như một callback Và khi đó this bên trong user.clickHandler sẽ không còn tham chiếu tới user object nữa, lúc này nó sẽ tham chiếu tới object, object mà đang gọi user.clickHandler method, Mà object mà ta gọi user.clickHandler chính là jQuery object $("button") thực thi click function

Để tránh bị như vậy nó cần được làm rõ khi mà context thay đổi, khi chúng ta thực thi một phương thức ở một phức thức khác, this keyword sẽ không còn được tham chiếu tới object định nghĩa ra nó nữa, nó sẽ tham chiếu tới object gọi method có định nghĩa this bên trong

Solution để fix:

Như ở ví dụ trên chúng ta cần this tham chiếu tới user object thay vì jQuery object $("button"), chúng ta có thể sử dụng bind(), apply(), call() method để set giá trị cho this

thay vì gọi như cách cũ

$ ("button").click (user.clickHandler);

ta sẽ sử dụng bind() method để gán lại giá trị cho this

$("button").click (user.clickHandler.bind (user)); // P. Mickelson 43

this trong closure

Một ví dụ khác về việc dùng this ở trong một inner method (hay còn gọi là closure). Một chú ý quan trọng đó là closure không thể access this của outer function bằng cách dùng keyword this bởi vì this chỉ có thể access bên trong function của chính nó không phải một inner function

var user = {
   tournament: "The Masters",
   data: [
     {name:"T. Woods", age:37},
     {name:"P. Mickelson", age:43}
   ],
   clickHandler:function () {
    // khi gọi `this.data` ở đây vẫn được bởi vì ở dưới ta gọi `user.clickHandler` nên this ở đây refer tới object gọi phương thức định nghĩa nó (chính là user)
     this.data.forEach (function (person) {
       // Nhưng ở đây thì không đây là một `anonymous function` và this bên trong function này không còn tham chiếu tới user object nữa
       console.log ("What is This referring to? " + this); //[object Window]​

       console.log (person.name + " is playing at " + this.tournament);
       // T. Woods is playing at undefined​
       // P. Mickelson is playing at undefined​
       })
     }
 }
 user.clickHandler(); // What is "this" referring to? [object Window]

this bên trong anonymous function không thể access this ở outer function, và nó thuộc về global window object

Solution để fix:

Để giải quyết vấn đề khi gọi this bên trong anonymous function truyền qua forEach method, đó là gán this object cho một biến trước khi chúng ta gọi forEach

var user = {
  tournament: "The Masters",
  data:[
    {name:"T. Woods", age:37},
    {name:"P. Mickelson", age:43}
  ],
  clickHandler:function (event) {
    var theUserObj = this;
    this.data.forEach (function (person) {
    console.log (person.name + " is playing at " + theUserObj.tournament);
    })
  }
}
user.clickHandler();
// T. Woods is playing at The Masters​
//  P. Mickelson is playing at The Masters

Khi this được sử dụng trong method mà method này được gán bởi một biến khác

  //data này ở global scope
  var data = [
    {name:"Samantha", age:12},
    {name:"Alexis", age:14}
  ];
​
  var user = {
    //  biến data này thuộc về user object
    data    :[
      {name:"T. Woods", age:37},
      {name:"P. Mickelson", age:43}
    ],
    showData:function (event) {
    var randomNum = ((Math.random () * 2 | 0) + 1) - 1; // random number between 0 and 1​
    console.log (this.data[randomNum].name + " " + this.data[randomNum].age);
    }
  }
​
  // gán method user.showData cho một biến
  var showUserData = user.showData;

  //​khi gọi showUserData function ở global scope, giá trị in ra sẽ là dữ liệu biến data của global scope chứ không phải `data` của user object
  showUserData (); // Samantha 12 (from the global data array)​

Solution để fix:

sử dụng bind method để set value cho this

var showUserData = user.showData.bind(user);
showUserData ();

Khi this được định nghĩa ở một biến nhưng lại được sử dụng bởi một biến khác

// Chúng ta có 2 object một có method `avg()` và object còn lại thì không
// Ta sẽ mượn `avg()` method
var gameController = {
  scores:[20, 34, 55, 46, 77],
    avgScore:null,
    players :[
      {name: "Tommy", playerID: 987, age: 23},
      {name: "Pau", playerID: 87, age: 33}
    ]
}
​
var appController = {
  scores:[900, 845, 809, 950],
  avgScore: null,
  avg: function () {
    var sumOfScores = this.scores.reduce (function (prev, cur, index, array) {
      return prev + cur;
    });
    this.avgScore = sumOfScores / this.scores.length;
  }
}
//avgScore của gameController object sẽ lấy giá trị avg của appController từ score của appController chứ không phải từ score của gameController object
gameController.avgScore = appController.avg();

this vẫn này không tham chiếu tới gameController object nó vẫn thuộc về appController bởi vì nó được gọi ở appController vậy để làm sao this có thể tham chiếu tới gamController object

*Solution để fix: *

sử dụng method apply()

 appController.avg.apply (gameController, gameController.scores);
​
 console.log (gameController.avgScore); // 46.4​
​
 console.log (appController.avgScore); // null

gameController mượn avg method của appController để gán giá trị cho avgScore của gameController object this bên trong appController.avg() sẽ được gán cho gameController object bởi vì chúng ta đã truyền gameController vào apply method ở tham số đầu tiên

Nguồn tham khảo

http://javascriptissexy.com/understand-javascripts-this-with-clarity-and-master-it/ You dont know JS


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí