JavaScript - 原型鍊
基本介紹
JavaScript 不像 Java 或是其他物件導向程式語言,JavaScript 是沒有 class 的 (ES6 的 class 也只是語法糖 ),儘管沒有 class 卻還是可以設計類似機制來達成差不多的功能
在開始之前可以先閱讀一下這兩篇文章 从设计初衷解释 JavaScript 原型链 、 Javascript 继承机制的设计思想。
JavaScript 繼承
兩篇文章皆有提到 JavaScript 當初設計時為非常簡易的腳本語言,而 JavaScript 有又有繼承的需求,但是開發者不打算引入 class 的概念,於是將 new
引入而在 JavaScript 中, new
之後並不是像 C++ 或是 Java 會調用 clss 中的 constructor ,而是直接調用 constructor 。
舉個 🌰 在 Java 中從 class 產生 instance 的話:
Point p = new Point();
而在 JavaScript 直接連接 constructor:
// constructor
function Person(name, age) {
this.name = name;
this.age = age;
}
var nick = new Person('nick', 18);
var peter = new Person('peter', 20);
也可以在 Person 中加入 method
function Person(name, age) {
this.name = name;
this.age = age;
this.log = function () {
console.log('Hello ' + this.name);
};
}
但是這樣做時,每個 instance 都會有各自的 this.log
,但是這個 this.log
都在做一樣的事情在每個 instance 都占用各自的空間。
為了避免這樣佔用空間就可以將這個 method 抽出變成每個 instance 都可以共用,於是我們將這個 mehthod 放入 prototype
。
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.log = function () {
console.log('Hello ' + this.name);
};
var nick = new Person('nick', 18);
var peter = new Person('peter', 20);
console.log(nick.log() === peter.log());
//true
原理
在上面的範例來講 nick.log()
JavaScript 是怎麼找到這個 function 的?
nick 本身是沒有 log 這個 function ,而根據 JavaScript 機制, nick 是 Person 的 instance 所以當 nick 找不到時,會從 Person.prototype
尋找, 而把 nick 與 Person.prototype
連接的方式 就是 __proto__
。
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.log = function () {
console.log('Hello ' + this.name);
};
var nick = new Person('nick', 18);
console.log(nick.__proto__ === Person.prototype);
//true
nick 的 __proto__
指向 Person.prototype,而Person.prototype.__proto__
又會指向 Object.prototype ,故此當 nick 找不到 log 這個 function 時就會靠 __proto__
往上找,一直找到 __proto__
為 null 時,這時就到了最上層。
而這一串透過 __proto__
所串起來的鍊就叫做原型鍊,透過原型鍊就可以達到類似繼承的功能,做到呼叫自己 parent 的 method 。
hasOwnProperty
另外如果想知道一個屬性是存在於 instance 上或是在原型鍊當中,可以使用 hasOwnProperty
:
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.log = function () {
console.log(this.name + ', age:' + this.age);
};
var nick = new Person('nick', 18);
console.log(nick.hasOwnProperty('log')); // false
console.log(nick.__proto__.hasOwnProperty('log')); // true
Instanceof
判斷 A 是不是 B 的 instance:
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.log = function () {
console.log(this.name + ', age:' + this.age);
};
var nick = new Person('nick', 18);
console.log(nick instanceof Person); // true
console.log(nick instanceof Object); // true
console.log(nick instanceof Array); // false
另外有幾個有趣現象先不研究,先筆記一下:
// 這兩個互為彼此的 instance
console.log(Function instanceof Object); // true
console.log(Object instanceof Function); // true
// Function 的 __proto__ 會指向 Function.prototype
// 而 Function.prototype 的 __proto__ 會指向 Object.prototype
console.log(Function.__proto__ === Function.prototype); // true
console.log(Function.__proto__.__proto__ === Object.prototype); //true
// Object 的 __proto__ 會指向 Function.prototype
console.log(Object.__proto__ === Function.prototype); // true