首先咱們看看原型編程範型至少包括如下基本規則。javascript
全部的數據都是對象。java
要獲得一個對象,不是經過實例化類,而是找到一個對象做爲原型並克隆它。 編程
對象會記住它的原型。函數
若是對象沒法響應某個請求,它會把這個請求委託給它本身的原型。 this
下面咱們來談論下javascript的原型繼承:spa
1. 全部的數據都是對象 :prototype
js有兩套機制類型:基本類型和對象類型,基本類型包括 undefined、number、boolean、string、function、object ;對象類型包括String、Number、Array、Date、function以及本身創建的對象等等。實際上,js除了underfined 不是對象,其餘的都是對象,那麼在 js中也必定會有一個根對象,這些對象追根溯源都來源於這個根對象 。對象
事實上,JavaScript 中的根對象是 Object.prototype 對象。Object.prototype 對象是一個空的 對象。咱們在 JavaScript 遇到的每一個對象,實際上都是從 Object.prototype 對象克隆而來的, Object.prototype 對象就是它們的原型。好比下面的 obj1 對象和 obj2 對象: 繼承
var obj1 = new Object();
var obj2 = {};
能夠利用 ECMAScript 5 提供的 Object.getPrototypeOf 來查看這兩個對象的原型: ip
console.log( Object.getPrototypeOf( obj2 ) === Object.prototype ); // 輸出:true
console.log( Object.getPrototypeOf( obj1 ) === Object.prototype ); // 輸出:true
2. 要獲得一個對象,不是經過實例化類,而是找到一個對象做爲原型並克隆它
var obj1 = new Object()或者 var obj2 = {}。此時,引擎內部會從 Object.prototype 上面克隆一個對象出來,咱們最終獲得的就是這個對象。 再來看看如何用 new 運算符從構造器中獲得一個對象
function Person(name) {
this.name = name;
};
Person.prototype.getName = function () {
return this.name;
};
var a = new Person('sven')
console.log(a.name); // 輸出:sven
console.log(a.getName()); // 輸出:sven
console.log(Object.getPrototypeOf(a) === Person.prototype);// 輸出:true
當使用 new 運算符來調用函數時,此時的函數就是一個構造器。 用new 運算符來建立對象的過程,實際上也只是先克隆 Object.prototype 對象(JavaScript 是經過克隆 Object.prototype 來獲得新的對象,但實際上並非每次都真正地克隆了一個新的對象。從 內存方面的考慮出發,JavaScript 還作了一些額外的處理 ),再進行一些其餘額 外操做的過程。
3. 對象會記住它的原型
若是請求能夠在一個鏈條中依次日後傳遞,那麼每一個節點都必須知道它的下一個節點。同理,JavaScript 語言中的原型鏈查找機制,每一個對象至少應該先記住它本身的原型。目前咱們一直在討論「對象的原型」,就 JavaScript 的真正實現來講,其實並不能說對象有 原型,而只能說對象的構造器有原型。對於「對象把請求委託給它本身的原型」這句話,更好 的說法是對象把請求委託給它的構造器的原型。那麼對象如何把請求順利地轉交給它的構造器 的原型呢? 前咱們一直在討論「對象的原型」,就 JavaScript 的真正實現來講,其實並不能說對象有 原型,而只能說對象的構造器有原型。對於「對象把請求委託給它本身的原型」這句話,更好 的說法是對象把請求委託給它的構造器的原型。那麼對象如何把請求順利地轉交給它的構造器 的原型呢?
JavaScript 給對象提供了一個名爲__proto__的隱藏屬性,某個對象的__proto__屬性默認會指 向它的構造器的原型對象,即{Constructor}.prototype。 實際上,__proto__就是對象跟「對象構造器的原型」聯繫起來的紐帶。正由於對象要經過 __proto__屬性來記住它的構造器的原型 。
var a = new Object();
console.log(a.__proto__ === Object.prototype); // 輸出:true
4. 若是對象沒法響應某個請求,它會把這個請求委託給它的構造器的原型
這條規則便是原型繼承的精髓所在。在 JavaScript 中,每一個對象都是從 Object.prototype 對象克隆而來的,若是是這樣的話, 咱們只能獲得單一的繼承關係,即每一個對象都繼承自 Object.prototype 對象,這樣的對象系統顯 4 然是很是受限的。 但對象構造 器的原型並不只限於 Object.prototype 上,而是能夠動態指向其餘對象。 這樣一來,當對象 a 需 要借用對象 b 的能力時,能夠有選擇性地把對象 a 的構造器的原型指向對象 b,從而達到繼承的 效果。下面的代碼是咱們最經常使用的原型繼承方式:
var obj = {name: 'sven'};
var A = function () {};
A.prototype = obj;// 輸出:sven
咱們來看看執行這段代碼的時候,引擎作了哪些事情。
首先,嘗試遍歷對象 a 中的全部屬性,但沒有找到 name 這個屬性。
查找 name 屬性的這個請求被委託給對象 a 的構造器的原型,它被 a.__proto__ 記錄着而且指向 A.prototype,而 A.prototype 被設置爲對象 obj。
在對象 obj 中找到了 name 屬性,並返回它的值。
當咱們指望獲得一個「類」繼承自另一個「類」的效果時,每每會用下面的代碼來模擬實現:
var A = function () {};
A.prototype = {name: 'sven'};
var B = function () {};
B.prototype = new A();
var b = new B();
console.log(b.name); // 輸出:sven
在來看看執行這段代碼的時候,引擎作了哪些事情。
首先,嘗試遍歷對象 b 中的全部屬性,但沒有找到 name 這個屬性。
查找 name 屬性的請求被委託給對象 b 的構造器的原型,它被 b.__proto__ 記錄着而且指向 B.prototype,而 B.prototype 被設置爲一個經過 new A()建立出來的對象。
在該對象中依然沒有找到 name 屬性,因而請求被繼續委託給這個對象構造器的原型 A.prototype。
在 A.prototype 中找到了 name 屬性,並返回它的值。