Toán tử new trong JavaScript

Toán tử new trong JavaScript

NỘI DUNG BÀI VIẾT

Toán tử new

Cách đơn giản nhất để hiểu toán tử new là hiểu xem nó làm những gì. Khi bạn sử dụng new, 4 thứ sau xảy ra:

  • Nó tạo ra một empty object mới
  • Nó bind this vào object mới đc tạo
  • Nó thêm một property tên là proto vào object mới đc tạo đó, property này trỏ đến constructor của prototype của object của function
  • Nó thêm return this vào cuối của function, do đó object mới được tạo đc return từ function

Thế là sao?

Nếu bạn ko hiểu mấy quy tắc trên thì cũng ko sao cả. Chúng ta sẽ đi dần dần. Đầu tiên tạo một constructor function tên là Student. Function này sẽ nhận 2 param là name và age. Nó sẽ set 2 thuộc tính đó vào giá trị của this.

function Student(name, age) {
  this.name = name;
  this.age = age;
}
Code language: JavaScript (javascript)

Xong, giờ thử invoke function này với toán tử new xem. Truyền cho nó 2 tham số là ‘John’ và 26 xem.

var first = new Student('John', 26);
Code language: JavaScript (javascript)

Vậy chuyện gì xảy ra nếu ta chạy đoạn code trên?

  1. Một object mới đc tạo ra, object first.
  2. This sẽ đc bind vào first, vậy nên mọi references đến this đều trỏ vào first.
  3. proto được thêm vào. Do đó first.proto sẽ trỏ vào Student.prototype.
  4. Sau cùng, object first mới đc tạo đó sẽ đc return thành biến first của ta. A new object is created — the first object. Thử test bằng console.log xem sao:
console.log(first.name);
// John
console.log(first.age);
// 26
Code language: JavaScript (javascript)

Ngon lành rồi, giờ thì đào sâu vào keyword proto nào.

Prototypes

Tất cả object của Javascript đều có một prototype. Tất cả object đều thừa kế method và properties từ prototype của chúng. Giờ test thử vài ví dụ nào.

function Student(name, age) {
  this.name = name;
  this.age = age;
}
Code language: JavaScript (javascript)

Để chứng tỏ rằng mỗi object đều có prototype, ta thử:

Student.prototype;
// Object {...}
Code language: JavaScript (javascript)

Một object đc trả về. Giờ thì thử tạo một student mới xem:

var second = new Student('Jeff', 50);
Code language: JavaScript (javascript)

Chúng ta đã sử dụng constructor của Student để tạo thêm một student thứ hai tên là Jeff. Và vì ta sử dụng toán tử new, proto property sẽ được thêm vào object thứ hai này. Nó sẽ trỏ vào constructor của object cha. Thử xem chúng có bằng nhau ko:

second.__proto__ === Student.prototype;
// true
Code language: JavaScript (javascript)

Cuối cùng, Student.prototype.constructor sẽ trỏ đến constructor của Student:

Student.prototype.constructor;
//  function Student(name, age) {
//    this.name = name;
//    this.age = age;
//  }
Code language: JavaScript (javascript)

Vì nó khá lằng nhằng nên hình này sẽ giúp bạn dễ hiểu hơn:

Như bạn thấy, constructor của Student (và tất cả constructor của các function khác nữa) có một property là .prototype. Cái prototype này có một object trong nó tên là .constructor, và nó trỏ lại constructor của function. Khi ta sử dụng toán tử new để tạo object mới, mỗi object đều có proto được liên kết ngược lại đến Student.prototype. Điều đó có gì quan trọng?

Nó quan trọng vì nó liên quan đến kế thừa. Prototype object được chia sẻ mới tất cả object mà được tạo bởi constructor của function đó. Điều đó có nghĩa là ta có thể thêm function và properties vào prototype đó và tất cả các object đều dùng được. Thử thêm một ví dụ nữa để sáng tỏ điều này:

Student.prototype.sayInfo = function(){
  console.log(this.name + ' is ' + this.age + ' years old');
}
Code language: JavaScript (javascript)

Ta vừa thêm một function vào prototype của Student. Bất kì student nào được tạo đều có thêm function .sayInfo này, thử xem sao:

second.sayInfo();
// Jeff is 50 years old
Code language: JavaScript (javascript)

Thử tạo thêm một student mới xem sao:

var third = new Student('Tracy', 15);
third.sayInfo();
// Tracy is 15 years old
Code language: JavaScript (javascript)

Kế thừa cũng chính là lý do vì sao ta có thể sử dụng function kiểu .toString(). Nghĩ lại thì, bạn chưa bao h viết function toString(), nhưng bạn vẫn dùng nó rất nhiều phải ko? Bởi vì hàm này, và cả nhiều hàm khác nữa là những hàm được viết sẵn trong prototype của Object. Tất cả các object ta tạo ra, về bản chất đều là delegate của prototype của Object mà thôi. Và tất nhiên, ta cũng có thể override mấy hàm đó bằng cách:

var name = {
  toString: function(){
    console.log('Not a good idea');
  }
};
name.toString();
// Not a good idea
Code language: JavaScript (javascript)

Đầu tiên object của ta sẽ check xem nó có hàm tên là toString(), rồi sau đó mới thử check prototype. Nhưng vì nó có hàm này nên nó chạy luôn mà ko dùng hàm toString của prototype.

Các bạn có thể tham khảo các bài viết hay về JavaScript tại đây.


Hãy tham gia nhóm Học lập trình để thảo luận thêm về các vấn đề cùng quan tâm.

Bình luận