成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專(zhuān)欄INFORMATION COLUMN

理解原型鏈和原型繼承

XboxYan / 2713人閱讀

摘要:原型鏈和構(gòu)造函數(shù)是一種面向?qū)ο蟮恼Z(yǔ)言,并且可以進(jìn)行原型繼承。來(lái)了極大的支持了工程化,它的標(biāo)準(zhǔn)讓瀏覽器內(nèi)部實(shí)現(xiàn)類(lèi)和類(lèi)的繼承構(gòu)造函數(shù)構(gòu)造函數(shù)調(diào)用父類(lèi)構(gòu)造函數(shù)現(xiàn)在瀏覽器對(duì)其支持程度還不高。

原型鏈

原型鏈比作用域鏈要好理解的多。

JavaScript中的每個(gè)對(duì)象,都有一個(gè)內(nèi)置的_proto_屬性。這個(gè)屬性是編程不可見(jiàn)的(雖然ES6標(biāo)準(zhǔn)中開(kāi)放了這個(gè)屬性,然而瀏覽器對(duì)這個(gè)屬性的可見(jiàn)性的支持不同),它實(shí)際上是對(duì)另一個(gè)對(duì)象或者null的引用。

當(dāng)一個(gè)對(duì)象需要引用一個(gè)屬性時(shí),JavaScript引擎首先會(huì)從這個(gè)對(duì)象自身的屬性表中尋找這個(gè)屬性標(biāo)識(shí),如果找到則進(jìn)行相應(yīng)讀寫(xiě)操作,若沒(méi)有在自身的屬性表中找到,則在_proto_屬性引用的對(duì)象的屬性表中查找,如此往復(fù),直到找到這個(gè)屬性或者_proto_屬性指向null為止。

這個(gè)_proto_的引用鏈,被稱(chēng)作原型鏈

注意,此處有一個(gè)性能優(yōu)化的問(wèn)題:往原型鏈越深處搜索,耗費(fèi)的時(shí)間越多。

原型鏈和構(gòu)造函數(shù)

JavaScript是一種面向?qū)ο蟮恼Z(yǔ)言,并且可以進(jìn)行原型繼承。

JavaScript中的函數(shù)有一個(gè)屬性prototype,這個(gè)prototype屬性是一個(gè)對(duì)象,它的一個(gè)屬性constructor引用該函數(shù)自身。即:

func.prototype.constructor === func; // ==> true  

這個(gè)屬性有什么用呢?我們知道一個(gè),一個(gè)函數(shù)使用new操作符調(diào)用時(shí),會(huì)作為構(gòu)造函數(shù)返回一個(gè)新的對(duì)象。這個(gè)對(duì)象的_proto_屬性引用其構(gòu)造函數(shù)的prototype屬性。

因此這個(gè)就不難理解了:

var obj = new Func();

obj.constructor == Func; // ==> true

還有這個(gè):

obj instanceof Func; // ==> true  

也是通過(guò)查找原型鏈上的constructor屬性實(shí)現(xiàn)的。

構(gòu)造函數(shù)生成的不同實(shí)例的_proto_屬性是對(duì)同一個(gè)prototype對(duì)象的引用。所以修改prototype對(duì)象會(huì)影響所有的實(shí)例。

“子類(lèi)”繼承實(shí)現(xiàn)的幾種方式

之所以子類(lèi)要加引號(hào),是因?yàn)檫@里說(shuō)“類(lèi)”的概念是不嚴(yán)謹(jǐn)?shù)摹avaScript是一門(mén)面向?qū)ο蟮恼Z(yǔ)言,但是它跟Java等語(yǔ)言不同,在ES6標(biāo)準(zhǔn)出爐之前,它是沒(méi)有類(lèi)的定義的。

但是熟悉Java等語(yǔ)言的程序員,也希望使用JavaScript時(shí),跟使用Java相似,通過(guò)類(lèi)生成實(shí)例,通過(guò)子類(lèi)復(fù)用代碼。那么在ES6之前,怎么做到像如下代碼一樣使用類(lèi)似"類(lèi)"的方式呢?

var parent = new Parent("Sam");

var child = new Children("Samson");

parent.say(); // ==> "Hello, Sam!"

child.say(); // ==> "Hello, Samson! hoo~~"

child instanceof Parent; // ==> true 

我們看到,這里我們把構(gòu)造函數(shù)當(dāng)做類(lèi)來(lái)用。

以下我們討論一下實(shí)現(xiàn)的幾種方式:

最簡(jiǎn)單的方式

結(jié)合原型鏈的概念,我們很容易就能寫(xiě)出這樣的代碼:

function Parent(name){
    this.name = name;
}

Parent.prototype.say = function(){
    console.log("Hello, " + this.name + "!");
}

function Children(name){
    this.name = name;
}

Children.prototype = new Parent();

Children.prototype.say = function(){
    console.log("Hello, " + this.name + "! hoo~~");
}

這個(gè)方式缺點(diǎn)很明顯:作為子類(lèi)的構(gòu)造函數(shù)需要依賴(lài)一個(gè)父類(lèi)的對(duì)象。這個(gè)對(duì)象中的屬性name根本毫無(wú)用處。

第一次改進(jìn)
// ...
Children.prototype = Parent.prototype;
// ...

這樣就不會(huì)產(chǎn)生無(wú)用的父類(lèi)屬性了。

然而,這樣的話(huà)子類(lèi)和父類(lèi)的原型就引用了同一個(gè)對(duì)象,修改子類(lèi)的prototype也會(huì)影響父類(lèi)的原型。

這時(shí)候我們發(fā)現(xiàn):

parent.say(); // ==> "Hello,Sam! hoo~~"

這第一次改進(jìn)還不如不改。

第二次改進(jìn)——臨時(shí)構(gòu)造函數(shù)/Object.create
function F(){  // empty  
}

F.prototype = Parent.prototype;

Children.prototype = new F();

// ...

parent.say(); // ==> "Hello, Sam!"
child.say();  // ==> "Hello, Samson! hoo~~"

這樣一來(lái),修改子類(lèi)的原型只是修改了F的一個(gè)實(shí)例的屬性,并沒(méi)有改變Parent.prototype,從而解決了上面的問(wèn)題。

在ES5的時(shí)代,我們還可以直接這樣:

Children.prototype = Object.create(Parent.prototype);

這里的思路是一樣的,都是讓子類(lèi)的prototype不直接引用父類(lèi)prototype。目前的現(xiàn)代瀏覽器幾乎已經(jīng)添加了對(duì)這個(gè)方法的支持。(但我們下面會(huì)仍以臨時(shí)構(gòu)造函數(shù)為基礎(chǔ))

但是細(xì)細(xì)思考,這個(gè)方案仍有需要優(yōu)化的地方。例如:如何讓父類(lèi)的構(gòu)造函數(shù)邏輯直接運(yùn)用到子類(lèi)中,而不是再重新寫(xiě)一遍一樣的?這個(gè)例子中只有一個(gè)name屬性的初始化,那么假設(shè)有很多屬性且邏輯一樣的話(huà),豈不是沒(méi)有做到代碼重用?

第三次改進(jìn)——構(gòu)造函數(shù)方法借用

使用apply,實(shí)現(xiàn)“方法重用”的思想。

function Children(name){
    Parent.apply(this, arguments);
    // do other initial things
}
“圣杯”模式

現(xiàn)在完整的代碼如下:

function Parent(name){
    this.name = name;
}

Parent.prototype.say = function(){
    console.log("Hello, " + this.name + "!");
}

function Children(name){
    Parent.apply(this, arguments);
    // do other initial things
}

function F(){  // empty  
}

F.prototype = Parent.prototype;

Child.prototype = new F();

Children.prototype.say = function(){
    console.log("Hello, " + this.name + "! hoo~~");
}

這就是所謂“圣杯”模式,聽(tīng)著很高大上吧?

以上就是ES3的時(shí)代,我們用來(lái)實(shí)現(xiàn)原型繼承的一個(gè)近似最佳實(shí)踐。

“圣杯”模式的問(wèn)題

“圣杯”模式依然存在一個(gè)問(wèn)題:雖然父類(lèi)和子類(lèi)實(shí)例的繼承的prototype對(duì)象不是同一個(gè)實(shí)例,但是這兩個(gè)prototype對(duì)象上面的屬性引用了同樣的對(duì)象。

假設(shè)我們有:

Parent.prototype.a = { x: 1};
// ...

那么即使是“圣杯”模式下,依然會(huì)有這樣的問(wèn)題:

parent.x // ==> 1
child.x  // ==> 1

child.x = 2;
parent.x // ==>2

問(wèn)題在于,JavaScript的拷貝不是 深拷貝(deepclone)

要解決這個(gè)問(wèn)題,我們可以利用屬性遞歸遍歷,自己實(shí)現(xiàn)一個(gè)深拷貝的方法。這個(gè)方法在這里我就不寫(xiě)了。

ES6來(lái)了

ES6極大的支持了工程化,它的標(biāo)準(zhǔn)讓瀏覽器內(nèi)部實(shí)現(xiàn)類(lèi)和類(lèi)的繼承:

class Parent {
    constructor(name) { //構(gòu)造函數(shù)
          this.name = name;
    }
    say() {
          console.log("Hello, " + this.name + "!");
    }
}

class Children extends Parent {
    constructor(name) { //構(gòu)造函數(shù)
        super(name);    //調(diào)用父類(lèi)構(gòu)造函數(shù)
        // ...
    }
    say() {
          console.log("Hello, " + this.name + "! hoo~~");
    }
}

現(xiàn)在瀏覽器對(duì)其支持程度還不高。但是這種寫(xiě)法的確省力不少。讓我們對(duì)未來(lái)拭目以待。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/90857.html

相關(guān)文章

  • 詳解javascript的類(lèi)

    摘要:原文地址詳解的類(lèi)博主博客地址的個(gè)人博客從當(dāng)初的一個(gè)彈窗語(yǔ)言,一步步發(fā)展成為現(xiàn)在前后端通吃的龐然大物。那么,的類(lèi)又該怎么定義呢在面向?qū)ο缶幊讨?,?lèi)是對(duì)象的模板,定義了同一組對(duì)象又稱(chēng)實(shí)例共有的屬性和方法。這個(gè)等同于的屬性現(xiàn)已棄用。。 前言 生活有度,人生添壽。 原文地址:詳解javascript的類(lèi) 博主博客地址:Damonare的個(gè)人博客 ??Javascript從當(dāng)初的一個(gè)彈窗語(yǔ)言,一...

    hufeng 評(píng)論0 收藏0
  • 詳解javascript的類(lèi)

    摘要:原文地址詳解的類(lèi)博主博客地址的個(gè)人博客從當(dāng)初的一個(gè)彈窗語(yǔ)言,一步步發(fā)展成為現(xiàn)在前后端通吃的龐然大物。那么,的類(lèi)又該怎么定義呢在面向?qū)ο缶幊讨?,?lèi)是對(duì)象的模板,定義了同一組對(duì)象又稱(chēng)實(shí)例共有的屬性和方法。這個(gè)等同于的屬性現(xiàn)已棄用。。 前言 生活有度,人生添壽。 原文地址:詳解javascript的類(lèi) 博主博客地址:Damonare的個(gè)人博客 ??Javascript從當(dāng)初的一個(gè)彈窗語(yǔ)言,一...

    marek 評(píng)論0 收藏0
  • 剖析JS的原型鏈和繼承

    摘要:接下來(lái)我們來(lái)聊一下的原型鏈繼承和類(lèi)。組合繼承為了復(fù)用方法,我們使用組合繼承的方式,即利用構(gòu)造函數(shù)繼承屬性,利用原型鏈繼承方法,融合它們的優(yōu)點(diǎn),避免缺陷,成為中最常用的繼承。 JavaScript是一門(mén)面向?qū)ο蟮脑O(shè)計(jì)語(yǔ)言,在JS里除了null和undefined,其余一切皆為對(duì)象。其中Array/Function/Date/RegExp是Object對(duì)象的特殊實(shí)例實(shí)現(xiàn),Boolean/N...

    darkerXi 評(píng)論0 收藏0
  • 體驗(yàn)javascript之美第七課 理解原型鏈和擴(kuò)展原型方法

    摘要:原型鏈理解第一件事你不用管其他語(yǔ)言,一句話(huà),你只要記住里面的對(duì)象包含一個(gè)原型,原型是啥,就是另外一個(gè)對(duì)象。原型就相當(dāng)于你家的車(chē)棚子,而你的那個(gè)自行車(chē)就是對(duì)象。萬(wàn)事萬(wàn)物皆對(duì)象有啥用一句話(huà),擴(kuò)展原型方法,給大家一到面試題,數(shù)組去重自己體會(huì)下。 概述 通過(guò)上節(jié)課的學(xué)習(xí),大家已經(jīng)會(huì)用一種json的方式定義對(duì)象了,其實(shí)這個(gè)就是傳說(shuō)中的單體模式,當(dāng)然這個(gè)大家不用記,關(guān)于設(shè)計(jì)模式暫時(shí)不用了解。但是總...

    wslongchen 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<