JavaScript中的原型繼承

首先咱們看看原型編程範型至少包括如下基本規則。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 屬性,並返回它的值。 

相關文章
相關標籤/搜索