Đâ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 this
từ 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 Person
mô 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 prototype
và new
từ khóa .
Vì vậy, trong bước này functions
, objects
và this
từ khóa, là tất cả chúng ta có.
Câu hỏi đầu tiên là làm thế nào this
từ khóa có thể hữu ích mà không cần sử dụng new
từ 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 new
từ 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 GLOBAL
trongNode.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 Object
và đí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 this
nà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- new
Từ khóa hoạt động như thế nào?
Đây là bước thứ hai để hiểu .prototype
chứ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 new
từ khóa và prototype
khi bạn sử dụng new
từ khóa. Vì vậy, khi chúng ta làm new Person("George")
, Person
hà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 getName
hà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 đó .prototype
và cũng có thể đối tượng này có nguyên mẫu nội bộ. và như thế.