對原型與原型鏈的理解

這部分是我長久以來習慣於直接忽略的部分,可是後面在面試和實習的過程當中發現這部分知識是很是有用的!因此我花了很久時間看書、參考博客、敲代碼,結合本身的理解將這些內容總結以下~javascript

最開始想要直觀的理解請直接參考阮一峯老師博客關於繼承的講解:http://www.ruanyifeng.com/blog/2011/06/designing_ideas_of_inheritance_mechanism_in_javascript.htmlhtml

 

原型對象

javascript語言是一種面向對象的語言,它沒有"子類"和"父類"的概念,裏面全部的數據類型都是對象,如何將這些對象聯繫起來呢?java

Brendan Eich在考慮設計繼承機制的時候,參考了C++和JAVA使用new命令,經過調用類的構造函數生成實例的方式,將new命令引入javascript。面試

C++的寫法是:閉包

  ClassName *object = new ClassName(param);ide

Java的寫法是:函數

  Person person = new Person();this

可是,javascript裏面沒有「類」這個概念,那麼,Brendan Eich決定直接在new後面跟一個構造函數,來生成實例。idea

構造函數是什麼?構造函數與其餘函數惟一的區別在於調用方式不一樣。任何函數只要經過new來調用就能夠做爲構造函數,它是用來建立特定類型的對象。spa

下面定義一個構造函數Female:

1 function Female(name){
2     this.name = name;
3     this.sex = 'female';  
4 }

經過new命令來生成一個person實例:

var person1 = new Female("Summer")

這裏,構造函數Female就是實例對象person1的原型!!!Female裏的this關鍵字就指的是person1這個對象!

 

new出來的person1對象此時已經和Female再無聯繫了!也就是說每個new出來的實例都有本身的屬性和方法的副本,是獨立的的!修改其中一個不會影響另外一個!

var person1 = new Female("Summer");
var person2 = new Female("Lily");

person2.sex = 'male';

console.log(person1.sex)      // female
console.log(person2.sex)      // male

可是,咱們但願構造函數中的sex屬性是一個共有屬性,那麼此時用這樣的方法,每一個實例中都有一個相同的sex屬性,會形成資源極大的浪費!

 那麼原型對象就即將登場了!Brendan Eich決定給每個構造函數都設置一個prototype屬性,這個屬性就指向原型對象。其實原型對象就只是個普通對象,裏面存放着全部實例對象須要共享的屬性和方法!因此,咱們把須要共享的放到原型對象裏,把那些不須要共享的屬性和方法存在在構造函數裏!

那麼上面的代碼可改寫以下:

        function Person(name,age){
            this.name = name;
        }
        Person.prototype.sex = 'female';

        var person1 = new Person("Summer");
        var person2 = new Person("Lily");
        
        console.log(person1.sex)      // female
        console.log(person2.sex)      // female

        Person.prototype.sex = 'male';

        console.log(person1.sex)      // male
        console.log(person2.sex)      // male

能夠看出,修改prototype屬性會影響它的全部實例的sex的值!!

實例一旦建立出來就會自動引用prototype對象的屬性和方法!因此實例對象的屬性和方法通常分爲兩種:一種是自身的,一種是引用自prototype的。

具體實現是這樣的:

      每當代碼讀取某個對象的某個屬性的時候,都會執行一次搜索。首先從對象實例自己開始,若是在實例中找到了該屬性,則返回該屬性的值,若是沒有找到,則順着原型鏈指針向上,到原型對象中去找,若是若是找到就返回該屬性值。

這裏要提一點,若是爲對象實例添加了一個屬性與原型中同名,則該屬性會屏蔽掉原型中的同名屬性,不會去修改它!使用delete能夠刪除實例中的屬性~(提到delete那要插一句~delete只能刪除對象下的屬性,不能刪除變量和參數!)

 原型鏈

事實上,js裏徹底依靠"原型鏈"(prototype chain)模式來實現繼承。

上面說完原型對象。下面要扒一扒__proto__、prototype、constructor

__proto__:事實上就是原型鏈指針!!

prototype:上面說到這個是指向原型對象的

constructor:每個原型對象都包含一個指向構造函數的指針,就是constructor

繼承實現方式:

 爲了實現繼承,__proto__會指向上一層的原型對象,而上一層的結構依然相似,那麼就利用__proto__一直指向Object的原型對象上!Object.prototype.__proto__ = null;表示到達最頂端。如此造成了原型鏈繼承。

下面有個圖解很是經典,我本身也手畫了一遍去理解,真的很是有效~

大體總結一下就是:

Object是做爲衆多new出來的實例的基類   function Object(){ [ native code ] }

Function是做爲衆多function出來的函數的基類    function Function(){ [ native code ] }

 

構造函數的__proto__(包括Function.prototype和Object.prototype)都指向Function.prototype

原型對象的__proto__都指向Object.prototype

Object.prototype.__proto__指向null

 

 

 

今兒老爹生日~下午給老爹作cake~晚上繼續總結js相關的面試題和閉包~

相關文章
相關標籤/搜索