Đây là một mô hình đối tượng dựa trên nguyên mẫu rất đơn giản sẽ được coi là một mẫu trong quá trình giải thích, chưa có bình luận nào:
function Person(name){
this.name = name;
}
Person.prototype.getName = function(){
console.log(this.name);
}
var person = new Person("George");
Có một số điểm quan trọng mà chúng ta phải xem xét trước khi đi qua khái niệm nguyên mẫu.
1- Cách các hàm JavaScript thực sự hoạt động:
Để thực hiện bước đầu tiên, chúng ta phải tìm hiểu xem các hàm JavaScript thực sự hoạt động như thế nào, như một lớp giống như hàm sử dụng thistừ khóa trong đó hoặc chỉ là một hàm thông thường với các đối số của nó, nó làm gì và trả về cái gì.
Giả sử chúng ta muốn tạo một Personmô hình đối tượng. nhưng trong bước này tôi sẽ cố gắng làm điều tương tự chính xác mà không cần sử dụng prototypevà newtừ khóa .
Vì vậy, trong bước này functions, objectsvà thistừ khóa, là tất cả chúng ta có.
Câu hỏi đầu tiên là làm thế nào thistừ khóa có thể hữu ích mà không cần sử dụng newtừ khóa .
Vì vậy, để trả lời rằng giả sử chúng ta có một đối tượng trống và hai hàm như:
var person = {};
function Person(name){ this.name = name; }
function getName(){
console.log(this.name);
}
và bây giờ không sử dụng newtừ khóa, làm thế nào chúng ta có thể sử dụng các chức năng này. Vì vậy, JavaScript có 3 cách khác nhau để làm điều đó:
a. Cách đầu tiên là gọi hàm như một hàm thông thường:
Person("George");
getName();//would print the "George" in the console
trong trường hợp này, đây sẽ là đối tượng bối cảnh hiện tại, thường là windowđối tượng toàn cầu trong trình duyệt hoặc GLOBALtrongNode.js . Điều đó có nghĩa là chúng ta sẽ có, window.name trong trình duyệt hoặc GLOBAL.name trong Node.js, với giá trị là "George".
b. Chúng ta có thể gắn chúng vào một đối tượng, như các thuộc tính của nó
- Cách dễ nhất để làm điều này là sửa đổi personđối tượng trống , như:
person.Person = Person;
person.getName = getName;
bằng cách này chúng ta có thể gọi chúng như:
person.Person("George");
person.getName();// -->"George"
và bây giờ personđối tượng giống như:
Object {Person: function, getName: function, name: "George"}
- Cách khác để đính kèm một thuộc tính vào một đối tượng là sử dụng prototypeđối tượng đó có thể tìm thấy trong bất kỳ đối tượng JavaScript nào có tên __proto__và tôi đã cố gắng giải thích một chút về phần tóm tắt. Vì vậy, chúng ta có thể nhận được kết quả tương tự bằng cách làm:
person.__proto__.Person = Person;
person.__proto__.getName = getName;
Nhưng theo cách này, những gì chúng ta thực sự đang làm là sửa đổi Object.prototype, bởi vì bất cứ khi nào chúng ta tạo một đối tượng JavaScript bằng chữ ( { ... }), nó sẽ được tạo dựa trên Object.prototype, có nghĩa là nó được gắn vào đối tượng mới được tạo như một thuộc tính có tên __proto__, vì vậy nếu chúng ta thay đổi nó , như chúng ta đã thực hiện trên đoạn mã trước đó, tất cả các đối tượng JavaScript sẽ được thay đổi, không phải là một cách thực hành tốt. Vì vậy, những gì có thể là thực hành tốt hơn bây giờ:
person.__proto__ = {
Person: Person,
getName: getName
};
và bây giờ các đối tượng khác đang trong hòa bình, nhưng dường như nó vẫn không phải là một thực hành tốt. Vì vậy, chúng ta vẫn còn một giải pháp nữa, nhưng để sử dụng giải pháp này, chúng ta nên quay lại dòng mã nơi personđối tượng được tạo ( var person = {};) sau đó thay đổi nó như sau:
var propertiesObject = {
Person: Person,
getName: getName
};
var person = Object.create(propertiesObject);
những gì nó đang tạo ra một JavaScript mới Objectvà đính kèm các propertiesObjectđến __proto__thuộc tính. Vì vậy, để đảm bảo bạn có thể làm:
console.log(person.__proto__===propertiesObject); //true
Nhưng điểm khó khăn ở đây là bạn có quyền truy cập vào tất cả các thuộc tính được xác định ở __proto__cấp độ đầu tiên của personđối tượng (đọc phần tóm tắt để biết thêm chi tiết).
như bạn thấy bằng cách sử dụng bất kỳ cách nào trong hai cách thisnày sẽ chỉ chính xác đếnperson đối tượng.
c. JavaScript có một cách khác để cung cấp hàm this, đó là sử dụng lệnh gọi hoặc áp dụng để gọi hàm.
Phương thức áp dụng () gọi một hàm với giá trị đã cho này và các đối số được cung cấp dưới dạng một mảng (hoặc một đối tượng giống như mảng).
và
Phương thức call () gọi một hàm với giá trị đã cho này và các đối số được cung cấp riêng lẻ.
theo cách này là sở thích của tôi, chúng ta có thể dễ dàng gọi các chức năng của mình như:
Person.call(person, "George");
hoặc là
//apply is more useful when params count is not fixed
Person.apply(person, ["George"]);
getName.call(person);
getName.apply(person);
3 phương thức này là các bước ban đầu quan trọng để tìm ra chức năng .prototype.
2- newTừ khóa hoạt động như thế nào?
Đây là bước thứ hai để hiểu .prototypechức năng. Đây là những gì tôi sử dụng để mô phỏng quá trình:
function Person(name){ this.name = name; }
my_person_prototype = { getName: function(){ console.log(this.name); } };
trong phần này tôi sẽ cố gắng thực hiện tất cả các bước mà JavaScript thực hiện, mà không sử dụng newtừ khóa và prototypekhi bạn sử dụng newtừ khóa. Vì vậy, khi chúng ta làm new Person("George"), Personhàm đóng vai trò là hàm tạo, Đây là những gì JavaScript làm, từng cái một:
a. trước hết, nó tạo ra một đối tượng trống, về cơ bản là một hàm băm trống như:
var newObject = {};
b. Bước tiếp theo mà JavaScript thực hiện là đính kèm tất cả các đối tượng nguyên mẫu vào đối tượng mới được tạo
chúng ta có my_person_prototypeở đây tương tự như đối tượng nguyên mẫu.
for(var key in my_person_prototype){
newObject[key] = my_person_prototype[key];
}
Đây không phải là cách mà JavaScript thực sự gắn các thuộc tính được xác định trong nguyên mẫu. Cách thực tế có liên quan đến khái niệm chuỗi nguyên mẫu.
a. & b. Thay vì hai bước này, bạn có thể có kết quả chính xác bằng cách thực hiện:
var newObject = Object.create(my_person_prototype);
//here you can check out the __proto__ attribute
console.log(newObject.__proto__ === my_person_prototype); //true
//and also check if you have access to your desired properties
console.log(typeof newObject.getName);//"function"
bây giờ chúng ta có thể gọi getNamehàm trong my_person_prototype:
newObject.getName();
c. sau đó nó đưa đối tượng đó cho hàm tạo,
chúng ta có thể làm điều này với mẫu của chúng tôi như:
Person.call(newObject, "George");
hoặc là
Person.apply(newObject, ["George"]);
sau đó hàm tạo có thể làm bất cứ điều gì nó muốn, bởi vì điều này bên trong nhà xây dựng mà là đối tượng vừa được tạo.
bây giờ là kết quả cuối cùng trước khi mô phỏng các bước khác: Object {name: "George"}
Tóm lược:
Về cơ bản, khi bạn sử dụng từ khóa mới trên một hàm, bạn đang gọi hàm đó và hàm đó đóng vai trò là hàm tạo, vì vậy khi bạn nói:
new FunctionName()
Javascript trong nội bộ làm cho một đối tượng, một băm rỗng và sau đó nó mang lại cho rằng đối tượng để các nhà xây dựng, sau đó các nhà xây dựng có thể làm bất cứ điều gì nó muốn, bởi vì này bên trong constructor đó là đối tượng vừa được tạo ra và sau đó nó mang lại cho bạn rằng đối tượng của quá trình nếu bạn chưa sử dụng câu lệnh return trong hàm của mình hoặc nếu bạn đặt dấu return undefined;ở cuối thân hàm.
Vì vậy, khi JavaScript tìm kiếm một thuộc tính trên một đối tượng, điều đầu tiên nó làm là tìm kiếm nó trên đối tượng đó. Và sau đó, có một thuộc tính bí mật [[prototype]]mà chúng ta thường có nó __proto__và thuộc tính đó là thứ JavaScript nhìn vào tiếp theo. Và khi nhìn qua __proto__, đối với nó lại là một đối tượng JavaScript khác, nó có __proto__thuộc tính riêng của nó, nó đi lên và đi lên cho đến khi đến điểm tiếp theo __proto__là null. Điểm là đối tượng duy nhất trong JavaScript mà __proto__thuộc tính của nó là null là Object.prototypeđối tượng:
console.log(Object.prototype.__proto__===null);//true
và đó là cách kế thừa hoạt động trong JavaScript.

Nói cách khác, khi bạn có một thuộc tính nguyên mẫu trên một hàm và bạn gọi một thuộc tính mới trên đó, sau khi JavaScript kết thúc việc nhìn vào đối tượng mới được tạo cho các thuộc tính, nó sẽ xem xét hàm đó .prototypevà cũng có thể đối tượng này có nguyên mẫu nội bộ. và như thế.