在JavaScript中,每個物件都有一個private attribute叫做__proto__
__proto__屬性存放的值是另一個物件
若物件A的__proto__屬性的值是設定成另一個物件B,則物件A就繼承了物件B的所有attributes以及methods
let phoebe = {
name: "Phoebe",
sayHi() {
console.log("說你好");
},
};
let darren = {
__proto__: phoebe,
};
console.log(darren.name); // -> Phoebe
darren.sayHi(); // -> 說你好
每個constructor function都可以設定prototype屬性(prototype屬性本質上來說,就是一個empty object)
所有從constructor function製作出來的物件, 其__proto__屬性都是自動指向constructor function的prototype屬性
function Person(name, age) {
this.name = name;
this.age = age;
this.sayHi = function () {
console.log(this.name + "說你好");
};
}
// console.log(Person.prototype) // -> {} empty object
let phoebe = new Person("Phoebe", 23); // -> phoebe.__proto__ => Person.prototype
let darren = new Person("Darren", 25); // -> darren.__proto__ => Person.prototype
obj.proto 以及 A.prototype都是 reference data type,所以true代表兩者指向同個記憶體位置
console.log(phoebe.__proto__ == Person.prototype); // -> true
constructor function的prototype屬性繼承attributes and methods的原理,就叫做Prototype Inheritance
若從constructor function製作出的每個物件都有相似的methods,我們可以把methods全部移動到constructor function的prototype屬性內部,而不是在個別的物件中重複定義methods
設定一個function
function Person(name, age) {
this.name = name;
this.age = age;
this.sayHi = function () {
console.log(this.name + "說你好");
};
}
Person.prototype.hello = function () {
console.log(this.name + "說你好");
};
phoebe.hello(); // -> phoebe說你好
console.log(phoebe.hello == darren.hello); // -> true
設定attributes
function Person(name, age) {
this.name = name;
this.age = age;
this.sayHi = function () {
console.log(this.name + "說你好");
};
}
Person.prototype.type = "人類";
console.log(phoebe.type); // -> 人類
使用constructor function來做物件的好處在於:
程式碼容易撰寫且維護,大量物件可以透過 constructor function 來製作
節省記憶體空間。兩個物件若有可以共用attritubes或methods,但分開製作,則會分別佔用記憶體內的不同位置。若使用constructor function 來製作,則兩個物件繼承來的attributes以及methods都是指向記憶體的相同位置
JS內建的資料類型都有繼承其他的Prototype
例如,[1, 2, 3]這個array
繼承了Array Prototype,而Array Prototype又繼承自Object Prototype
這種Prototype不斷往上連結的結果就叫做Prototype Chain
JavaScript中的所有物件最後的 Prototype Chain 都會連到一個叫做 Object Prototype的地方,是 Prototype Chain 的終點
一個 constructor function A 可以透過兩個設定來繼承另一個 constructor function B 的 prototype 物件:
B.call(this, args1, …, argsN)
。我們可以透過這段程式碼直接將 B 所設定的屬性套給 A 做使用A.prototype = Object.create(B.prototype)
。 Object.create()可以創建一個全新的物件這樣一來,所有在 B.prototype 內部的 attributes 與 methods 都可以套用給 A.prototype
所有 A.prototype 所設定的額外的 attributes 與 methods 都需要寫在 A.prototype = Object.create(B.prototype)這行程式碼的下方
不能寫 A.prototype = B.prototype 是因為,constructor.prototype 是reference data type
// 如果寫
A.prototype = B.prototype;
A.prototype.add = function() {}
// 則A, B兩個prototype都指向記憶體的相同位置,且兩個prototype都有add()這個methods了
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.sayHi = function () {
console.log(this.name + "說你好");
};
function Student(name, age, major, grade) {
Person.call(this, name, age);
this.major = major;
this.grade = grade;
}
Student.prototype = Object.create(Person.prototype);
Student.prototype.study = function () {
console.log(this.name + "正在努力讀" + this.major);
};
let Phoebe = new Student("Phoebe Lee", 23, "Math", 3.5);
Phoebe.sayHi();// -> Phoebe Lee說你好
Phoebe.study();// -> Phoebe Lee正在努力讀Math
let Darren = new Person("Darren Lo", 25);
Darren.study(); // -> Uncaught TypeError: Darren.study is not a function
如果沒有寫在 Object.create 下面
Student.prototype.study = function () {
console.log(this.name + "正在努力讀" + this.major);
};
Student.prototype = Object.create(Person.prototype);
Phoebe.study(); // -> Uncaught TypeError: Phoebe.study is not a function
下一篇文章學習進階的靜態與動態網頁、網頁開發工具。