JS 原型鍊(Prototype Chain)

物件擁有自己的屬性,他可以繼承原型,而原型也可以繼承其他原型,原先的物件可以使用.取用屬性,而如果在原本物件找不到此屬性,他就會向上查找直到找到屬性或是null(找到 null 就會報錯),而如果有從一個原型新增出兩個實體,他們會共用同一個原型的方法

特性

  1. 一樣具有物件特性
  2. 向上查找
  3. 原型可共用發方法及屬性

原型在哪裡

以下寫法為非正規寫法

以下範例可以看到陣列的原型,也可以為他的原型加上屬性,因為屬性是共用的,假如你新增其他陣列也可以使用getLast()方法。

建構式自定義原型

利用函式建構物件,使用Prototype製作原型功能,在過去無規範如何製作原型功能,所以大多採用__proto__製作,後來ECMAScript 規範要使用[[Prototype]]製作原型功能,
但之前開發者大多使用__proto__原型所以瀏覽器還保留此方法,所以如果現在看到[[Prototype]]就是瀏覽器還沒更新前的__proto__是往上追朔的原型。

proto 不屬於正規 JS,他是瀏覽器的方法,會導致同層所有資料被賦予新方法

prototype 是正規的JS方法,在為建構函式新增方法時、原型作多層串接時都會使用

以下範例使用函式建構物件

// 利用含有 name 與 city 屬性的 human 函式,建立一個 Joe 物件:
function human(name, city) {
this.name = name;
this.city = city;
}

var Joe = new human("joe", "Tiawan");

// 接著針對 human 函式的原型添加屬性
human.prototype.work = function () {
console.log(`${this.name}工作`);
};

console.log(Joe.work()); // joe工作

// __proto__ 物件上新增(維護難)
// prototype 函式上新增(建議在原型屬性上新增)
console.log(human.prototype === Joe.__proto__); // true

Object.create 建立多層繼承

使用Object.create繼承方法,
當繼承函式執行時,this 值指向繼承的物件,而不是在函式內擁有屬性的原型物件。

為啥總要修正 constructor。補充

function Animal(family) {
this.kingdom = "動物界";
this.family = family;
}

Animal.prototype.move = function () {
console.log(`${this.name}走路`);
};

function Cat(name, color, size) {
Animal.call(this, "貓科"); // 只繼承原型,所以需補上建構函式
this.name = name;
this.color = color;
this.size = size;
}

Cat.prototype = Object.create(Animal.prototype); // 將Animal.prototype當作原型使用
Cat.prototype.constructor = Cat; // 將Cat建構函式補回去,否的話constructor會是Animal建構函式
Cat.prototype.detail = function () {
console.log(`名子:${this.name}, 顏色:${this.color}, 大小: ${this.size}`);
};

var Amy = new Cat("艾米", "棕色", "小型");
var Kumo = new Cat("哭某", "白色", "大型");
Amy.detail(); // 名子:艾米, 顏色:棕色, 大小: 小型
Amy.move(); // 艾米走路

Kumo.detail(); // 名子:哭某, 顏色:白色, 大小: 大型
Kumo.move(); // 哭某走路

使用 ES6 新語法 class 建立物件

將上述的繼承與建立物件改成使用 class 語法,類別的主體指的是被大括號({})包含的部分,你可以在這裡面定義類別成員(members),例如方法(methods)或建構子(constructors)。

若在子類別中有建構子(constructor),要使用 this 前則必須先呼叫 super()函式。

class MDN

class Animal {
constructor(family) {
this.kingdom = "動物界";
this.family = family;
}

echoFamily() {
console.log(`${this.name}走路`);
}
}

class Cat extends Animal {
constructor(name, color, size) {
super("貓科");
this.name = name;
this.color = color;
this.size = size;
}

detail() {
console.log(`名子:${this.name}, 顏色:${this.color}, 大小: ${this.size}`);
}
}

var Amy = new Cat("艾米", "棕色", "小型");

Amy.echoFamily();
Amy.detail();