prototype原型

原型

每一個(gè)JavaScript對(duì)象(null除外)都和另一個(gè)對(duì)象相關(guān)聯(lián)。“另一個(gè)”對(duì)象就是我們熟知的原型,每一個(gè)對(duì)象都從原型繼承屬性?!禞avaScript權(quán)威指南》

每創(chuàng)建一個(gè)對(duì)象,對(duì)象上就會(huì)有一個(gè)[[prototype]]屬性指向該對(duì)象的原型,但因?yàn)閇[prototype]]是內(nèi)置屬性,JavaScript無(wú)法直接進(jìn)行訪問,所以有些瀏覽器自己實(shí)現(xiàn)了__proto__屬性讓我們能夠直接訪問到對(duì)象的原型,在ES5之后,添加了Object.getPrototypeOf()函數(shù)來讓我們能直接獲取指定對(duì)象的原型。

  • 當(dāng)使用對(duì)象字面量來創(chuàng)建對(duì)象時(shí),[[prototype]]指向Object.prototype
var obj = {}
console.log(Object.getPrototypeOf(obj)=== Object.prototype)  // true
上例圖解

上例可以看到,使用字面量來創(chuàng)建對(duì)象,那么創(chuàng)建出的對(duì)象的原型和Object的prototype指向的是同一塊內(nèi)存區(qū)域。

  • 當(dāng)使用Object.create()來創(chuàng)建對(duì)象時(shí),對(duì)象的原型就是指定的對(duì)象。
var obj = {a:1}
var obj2 = Object.create(obj)
console.log(Object.getPrototypeOf(obj)=== Object.prototype)  // true
console.log(Object.getPrototypeOf(obj2) === obj)  // true
上例圖解

上例可以看到,使用Object.create創(chuàng)建對(duì)象,那么創(chuàng)建出的對(duì)象的原型指向的是Object.create()中第一個(gè)參數(shù)(當(dāng)?shù)谝粋€(gè)參數(shù)為null的時(shí)候,創(chuàng)建出來的是一個(gè)沒有原型的空對(duì)象)

  • 當(dāng)使用new來創(chuàng)建對(duì)象時(shí),對(duì)象的原型就是構(gòu)造函數(shù)的原型
    function Foo() {}
    Foo.prototype.add = function(){}
    var fn = new Foo()
    fn.a = 1
    console.log(Object.getPrototypeOf(fn) === Foo.prototype)
上例圖解

上例可以看到,使用new來創(chuàng)建對(duì)象,那么對(duì)象的原型指向的就是構(gòu)造函數(shù)的原型。

無(wú)論什么時(shí)候,只要?jiǎng)?chuàng)建了一個(gè)新函數(shù),就會(huì)根據(jù)一組特定的規(guī)則為該函數(shù)創(chuàng)建一個(gè)prototype屬性,這個(gè)屬性指向函數(shù)的原型對(duì)象。在默認(rèn)情況下,所有原型對(duì)象都會(huì)自動(dòng)獲得一個(gè)constructor(構(gòu)造函數(shù))屬性,這個(gè)屬性包含一個(gè)指向prototype屬性所在函數(shù)的指針。——《JavaScript高級(jí)程序設(shè)計(jì)》

上面介紹了幾種原型產(chǎn)生的方式,下面來介紹一下原型鏈?zhǔn)鞘裁?,以及原型鏈的作用?/p>

原型鏈

JavaScript主要通過原型鏈實(shí)現(xiàn)繼承。原型鏈的構(gòu)建是通過將一個(gè)類型的實(shí)例賦值給另一個(gè)構(gòu)造函數(shù)的原型實(shí)現(xiàn)的。這樣,子類型就能夠訪問超類型的所有屬性和方法,這一點(diǎn)和基于類的繼承很相似。——《JavaScript高級(jí)程序設(shè)計(jì)》

通過構(gòu)造函數(shù)來創(chuàng)建原型鏈

我們前面說過,使用new創(chuàng)建出來的對(duì)象會(huì)有一個(gè)[[prototype]],指向構(gòu)造函數(shù)的prototype,所以我們可以利用這一點(diǎn)來創(chuàng)建出我們需要的原型鏈。

function Animal() {}
Animal.prototype.sing = function () {
  console.log(this.voice)
}

function Dog() {
  this.voice = '汪汪汪'
}
Dog.prototype = new Animal()
Dog.prototype.run = function () {
  console.log("start running")
}

function Bird() {
  this.voice = '啾啾啾'
}
Bird.prototype = new Animal()
Bird.prototype.fly = function () {
  console.log("start flying")
}

var dog = new Dog()
var bird = new Bird()

上例定義了三個(gè)類型,Animal、Dog和Bird。Animal的prototype上有一個(gè)sing方法,Dog和Bird都有一個(gè)voice屬性并且prototype上都有一個(gè)Animal的實(shí)例,Dog的prototype上有一個(gè)run方法,Bird的prototype上有一個(gè)fly方法。
下面看圖解:

上例圖解

由圖可知,構(gòu)造函數(shù)Dog中定義在this上的屬性會(huì)產(chǎn)生在實(shí)例dog上,實(shí)例dog的[[prototype]]指向構(gòu)造函數(shù)Dog的原型,而構(gòu)造函數(shù)Dog的[[prototype]]指向了Animal的原型。當(dāng)實(shí)例dog想要調(diào)用sing方法的時(shí)候,先查找自身是否具有這個(gè)方法,沒有的話就通過[[prototype]]到Dog的原型上進(jìn)行查找,在Dog的原型中沒有找到,那就再通過Dog的原型上的[[prototype]]到Animal的原型上進(jìn)行查找,終于在Animal的原型中找到了這個(gè)方法,然后就可以開始調(diào)用sing方法了。

通過Object.create來創(chuàng)建原型鏈

我們使用Object.create來創(chuàng)建一個(gè)和上面一樣效果的原型鏈:

var Animal = {
  sing: function () {
    console.log(this.voice)
  }
}

var Dog = Object.create(Animal)
Dog.voice = '汪汪汪',
Dog.run = function () {
  console.log('start running')
}

var Bird = Object.create(Animal)
Bird.voice = '啾啾啾'
Bird.fly = function () {
  console.log('start flying')
}

var dog = Object.create(Dog)
dog.sing()  // 汪汪汪

var bird = Object.create(Bird)
Bird.sing()  // 啾啾啾
上例圖解

比較兩幅圖解可以看出,使用Object.create創(chuàng)建出的原型鏈明顯要比使用構(gòu)造函數(shù)來創(chuàng)建出的原型鏈簡(jiǎn)單直觀很多,所以在我個(gè)人看來比較喜歡使用Object這樣來構(gòu)建需要的原型鏈。

原型的終點(diǎn)

所有原型鏈都會(huì)指向Object.prototype,而Object.prototype的[[prototype]]為null,所以原型鏈的終點(diǎn)就是null

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容