你知道的JS原型鏈(上)--對象與原型

你知道的JS原型鏈--對象與原型

最近一直在以忙為借口犯懶,到了清明節(jié)終于捱不過去了,所以還是準(zhǔn)備寫一篇文章來總結(jié)一下。最近開發(fā)的一個項目涉及到一些關(guān)于es6繼承的問題。得益于JavaScript強(qiáng)大的原型機(jī)制,我們可以很輕易的模仿出類的特性,當(dāng)然由于是基于原型實現(xiàn)的class語法糖,所以我們可以做出一些特別騷氣的操作,比如動態(tài)繼承。
例如

class A {
    constructor(){
        console.log('this is A');
    }
}

// class factory
function InheritMaker(constructor){
    return class B extends constructor {
        constructor(){
            super();
            console.log('this is B');
        }
    }
}

const a = new A(); // this is A

const B = InheritMaker(A);
const b = new B(); // this is A; this is B

說實話,我一直在思考這種方式到底是不是違反了類的初衷。類是靜態(tài)的,但是我們在 JavaScript中可以在運行時動態(tài)選擇繼承,這使得類的行為變得不確定: 我無法知道我的類到底會實現(xiàn)什么樣子的功能,只用運行時才知道。這種見鬼的實現(xiàn)方式在Java中絕對會報錯報到死,但是在JavaScript中卻可以平穩(wěn)運行。

說了這么多,這種靈活不失騷氣的實現(xiàn)方式還是得益于JavaScript中強(qiáng)大的原型機(jī)制。接下來,我希望來說清楚JavaScript中的原型到底是怎么回事,以及拋磚引玉的對于es6class實現(xiàn)進(jìn)行一些粗淺的探討。

對象與原型

我最一開始接觸JavaScript的時候不知道什么是原型,把它和對象當(dāng)作兩個概念來看待。
我們都知道,一個類new出來的實例是一個對象。

我們來看下EcmaScript對于對象的定義:

無序?qū)傩缘募?,其屬性可以包含基本值,對象或者函?shù)

原型就是無需屬性的集合,原型就是對象,原型就是實例

下面讓我們來拋開es6談一談對象與原型。

我們都知道,對象是我們用class ‘new’出來的,例如我們想實現(xiàn)創(chuàng)建兩個人,張三和李四,他們都擁有相同的屬性,但是屬性的值不同,所以我們將他們歸為一個‘類’,在某些靜態(tài)語言中大致長這樣:

class People {
    constructor(string name, int age) {
        this.name = name;
        this.age = age;
    }
}

const zhangsan = new People('張三', 12);
const lisi = new People('李四', 14);

這樣兩個人就創(chuàng)建好了。

但是JavaScript沒有類的概念怎么破?

下面用es5實現(xiàn)了同樣的 效果:

function Peole(name, age) {
    this.name = name;
    this.age = age;
}

var zhangsan = new People('張三', 12);
var lisi = new People('李四', 14);

上面創(chuàng)建了一個構(gòu)造函數(shù),實現(xiàn)了相同的效果。

對于JavaScript來說,People是一個實例,它的構(gòu)造函數(shù)是Function,因此它擁有__proto__,又是(構(gòu)造)函數(shù),所以擁有prototype,值得說明的一點是只有函數(shù)擁有prototype。

那么prototype__proto__又有什么區(qū)別呢?

prototype & proto

JavaScript中有兩個指針,prototype__proto__,我在初學(xué)的時候一直對于這兩個指針一直十分困惑,現(xiàn)在我希望可以解釋清楚這些事情。

對于People構(gòu)造函數(shù)來說,它在被創(chuàng)建的時候擁有prototype屬性,prototypeconstructor指向它本身:

image.png

如果用一種不太準(zhǔn)確但容易理解的方式來解釋,prototype才是People的類,上面擁有People所定義的一切方法,例如我們舉個例子:

class People {
    constructor(name, age){
        this.name = name;
        this.age = age;
    }
    
    changeName(newName){
        this.name = newName;
    }
}

我們在上面多聲明了一個方法changeName,因此上面的圖改為:

image.png

這就是prototype

但是我們也說了People不僅僅是個構(gòu)造函數(shù),它還是Function的實例,對于一個實例來說,它需要知道創(chuàng)建它的類是什么。

注意這里非常容易糊涂:

prototype指向的是它作為構(gòu)造函數(shù)的類
_proto_指向的是創(chuàng)建它的類

其實這也非常容易理解,因為對于JavaScript來說,函數(shù)也是一個對象。也就是說函數(shù)也是被‘new’出來的。創(chuàng)建它的類是Function

就是下面的這個Function:

const People = new Function();

因此上面的圖可以更改為:

image.png

同樣的,F(xiàn)unction類的構(gòu)造函數(shù)也是一個函數(shù)。。所以Function構(gòu)造函數(shù)prototype指向Function類,而它的__proto__也指向它的類:

image.png

因此你可以試一下:

Function.prototype === Function.__proto__; // true

我們剛才也說過了,prototype 也是一個對象,也是被'new'出來的,因為是對象,所以創(chuàng)造它的構(gòu)造函數(shù)是Object,因此上圖可以更改為:

image.png

因此就出現(xiàn)了一個問題,創(chuàng)建Object的類是個毛?

說的再玄幻一點就是創(chuàng)建對象的是個什么玩應(yīng)?

是null

從無到有。

小結(jié)

因此原型的本質(zhì)可以概括為:

JavaScript中的類也是個對象

當(dāng)然這不太準(zhǔn)確,我會在下節(jié)做闡述,先這么理解就好。

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

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

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