摘要:此時(shí)的原型對(duì)象包括一個(gè)指向另一個(gè)原型的指針,相應(yīng)的,另一個(gè)原型中的指向另一個(gè)構(gòu)造函數(shù)。這種關(guān)系層層遞進(jìn),就通過一個(gè)原型對(duì)象鏈接另一個(gè)構(gòu)造函數(shù)的原型對(duì)象的方式實(shí)現(xiàn)了繼承。
讀這篇之前,最好是已讀過我前面的關(guān)于對(duì)象的理解和封裝類的筆記。
第6章我一共寫了3篇總結(jié),下面是相關(guān)鏈接:
讀《javaScript高級(jí)程序設(shè)計(jì)-第6章》之理解對(duì)象
讀《javaScript高級(jí)程序設(shè)計(jì)-第6章》之封裝類
原型鏈最簡(jiǎn)單的理解就是:原型對(duì)象指向另一個(gè)構(gòu)造函數(shù)的實(shí)例。此時(shí)的原型對(duì)象包括一個(gè)指向另一個(gè)原型的指針,相應(yīng)的,另一個(gè)原型中的constructor指向另一個(gè)構(gòu)造函數(shù)。這種關(guān)系層層遞進(jìn),就通過一個(gè)原型對(duì)象鏈接另一個(gè)構(gòu)造函數(shù)的原型對(duì)象的方式實(shí)現(xiàn)了繼承。
下面用代碼和圖來詳細(xì)分析一下原型鏈中的各種關(guān)系:
function SuperType(){ ??? this.property = true; } SuperType.prototype.getSuperValue = function(){ ??? return this.property; }; function SubType(){ ??? this.subproperty = false; } //inherit from SuperType SubType.prototype = new SuperType(); SubType.prototype.getSubValue = function (){ ??? return this.subproperty; }; var instance = new SubType(); alert(instance.getSuperValue());?? //true alert(instance.getSubValue());?? //false alert(instance instanceof Object);????? //true alert(instance instanceof SuperType);?? //true alert(instance instanceof SubType);???? //true alert(Object.prototype.isPrototypeOf(instance));??? //true alert(SuperType.prototype.isPrototypeOf(instance)); //true alert(SubType.prototype.isPrototypeOf(instance));?? //true console.log(new SuperType()); console.log(instance);
下圖是上面代碼中打印出來的new SuperType()和instance的分析:
從上面的分析我們看到的原型鏈:
SubType的原型里有指向SuperType的原型的指針,SuperType的原型里有指向Object的原型的指針。
也可以看紅皮書里的圖:
訪問屬性的搜索過程:
當(dāng)以讀取模式訪問一個(gè)構(gòu)造函數(shù)(SubType)的實(shí)例的屬性時(shí),首先會(huì)在實(shí)例中搜索實(shí)例屬性。如果沒找到該屬性,則會(huì)繼續(xù)搜索實(shí)例的原型;SubType繼承了SuperType,那么實(shí)例的原型是另一個(gè)構(gòu)造函數(shù)(SuperType)的實(shí)例,搜索實(shí)例的原型也就是在SuperType的實(shí)例中搜索該屬性,沒找到繼續(xù)搜索SuperType的原型;SuperType繼承了Object,以此遞進(jìn),一層層搜索,直到找到或者搜到了原型鏈的末端停下來。
判斷原型和實(shí)例的關(guān)系
(1)instanceof
實(shí)例的原型鏈中出現(xiàn)過待檢測(cè)的構(gòu)造函數(shù),就會(huì)返回true
alert(instance instanceof Object);????? //true alert(instance instanceof SuperType);?? //true alert(instance instanceof SubType);???? //true
(2)isPrototypeOf()方法
待檢測(cè)對(duì)象出現(xiàn)在instance的原型鏈中,就會(huì)返回true
alert(Object.prototype.isPrototypeOf(instance));??? //true alert(SuperType.prototype.isPrototypeOf(instance)); //true alert(SubType.prototype.isPrototypeOf(instance));?? //true
注意事項(xiàng)
(1)給原型添加方法的代碼一定要放在替換原型的語句之后。也就是
SubType.prototype = new SuperType();這句代碼一定要先寫,在寫下面的代碼 //new method SubType.prototype.getSubValue = function (){ ??? return this.subproperty; }; //override existing method SubType.prototype.getSuperValue = function (){ ??? return false; };
(2)在通過原型鏈實(shí)現(xiàn)繼承時(shí),不能使用對(duì)象字面量為原型添加屬性,因?yàn)檫@會(huì)重寫原型鏈(具體請(qǐng)看理解對(duì)象篇里的一、創(chuàng)建對(duì)象)。
如下:
function SuperType(){ ??? this.property = true; } SuperType.prototype.getSuperValue = function(){ ??? return this.property; }; function SubType(){ ??? this.subproperty = false; } //繼承了 SuperType SubType.prototype = new SuperType(); //使用字面量添加新方法,會(huì)導(dǎo)致上一行代碼無效 SubType.prototype = { ??? getSubValue : function (){ ??????? return this.subproperty; ??? }, ??? someOtherMethod : function (){ ??????? return false; ??? } }; var instance = new SubType(); alert(instance.getSuperValue());?? //error!
其實(shí)這兩個(gè)注意事項(xiàng),只要你明白了(理解對(duì)象篇里的一、創(chuàng)建對(duì)象)后,根本不需要解釋。
原型鏈的問題
(1)沒有辦法在不影響所有對(duì)象實(shí)例的情況下,給超類型的構(gòu)造函數(shù)傳遞參數(shù)。
(2)在另一篇筆記封裝類原型模式中提到過,原型中的屬性是被共享的,但如果屬性的值時(shí)引用類型,會(huì)有問題的。而在繼承時(shí),原型實(shí)際上會(huì)是另一個(gè)類型的實(shí)例(這個(gè)實(shí)例包含引用類型值的實(shí)例屬性),那么原先的這個(gè)實(shí)例的實(shí)例屬性就會(huì)成為現(xiàn)在的原型屬性了,就會(huì)出現(xiàn)同樣的問題了。共享了引用類型值的屬性。
直接上代碼吧:
function SuperType(name){ ??? this.name = name; } function SubType(){? ??? //繼承了 SuperType ,同時(shí)還傳遞了參數(shù) ??? SuperType.call(this, "Nicholas"); ??? ??? //實(shí)例屬性 ??? this.age = 29; } var instance = new SubType(); alert(instance.name);??? //"Nicholas"; alert(instance.age);???? //29
如上寫法就解決了原型鏈里的兩個(gè)問題了,為什么呢?請(qǐng)看下面的講解:
SuperType,如果你用new調(diào)用它是構(gòu)造函數(shù),但你不用new,它就是個(gè)普通函數(shù)。SuperType.call(this, "Nicholas");不但傳遞了參數(shù),還綁定了子類的作用域,就相當(dāng)于SuperType方法在幫助定義子類的實(shí)例屬性。也就是說,即使SuperType的中定義的屬性里有引用類型值,也不會(huì)成為子類SubType的原型屬性,仍然時(shí)實(shí)例屬性。我們要時(shí)刻記住實(shí)例屬性是每個(gè)實(shí)例所私有的,而原型屬性是會(huì)被所有實(shí)例所共享的。
當(dāng)然這也寫也不完美,問題顯而易見,和構(gòu)造函數(shù)模式同樣的問題。
三、組合繼承組合繼承,就像是封裝類里的把構(gòu)造函數(shù)模式和原型模式組合使用是一樣的。這里是把原型鏈和借用構(gòu)造函數(shù)相組合。
簡(jiǎn)單來說就是:使用原型鏈實(shí)現(xiàn)對(duì)原型屬性和方法的繼承,通過借用構(gòu)造函數(shù)實(shí)現(xiàn)對(duì)實(shí)例屬性的繼承(父類的實(shí)例屬性變成子類的實(shí)例屬性)。
還是上代碼吧:
function SuperType(name){ ??? this.name = name;??? this.colors = ["red", "blue", "green"]; } SuperType.prototype.sayName = function(){ ??? alert(this.name); }; function SubType(name, age){? ??? SuperType.call(this, name); ??? this.age = age; } SubType.prototype = new SuperType(); SubType.prototype.sayAge = function(){ ??? alert(this.age); }; var instance1 = new SubType("Nicholas",29); instance1.colors.push("black"); alert(instance1.colors);?//"red,blue,green,black" instance1.sayName();????? //"Nicholas"; instance1.sayAge();?????? //29 var instance2 = new SubType("Greg", 27); alert(instance2.colors);? //"red,blue,green" instance2.sayName();????? //"Greg"; instance2.sayAge();?????? //27
解釋:
下圖是instance1的打印
我們可以看到instance1具有了父類SuperType的實(shí)例屬性name 、colors,但是子類的原型是父類的實(shí)例,所以原型中仍存在父類的實(shí)例屬性,但是子類已經(jīng)有了同樣的實(shí)例屬性name和colors,所以子類原型中的這兩個(gè)屬性就被屏蔽了。從子類訪問它的name和colors屬性只會(huì)訪問到它的實(shí)例屬性。
組合繼承是javaScript中最常用的繼承模式。而且instance和isPrototypeOf()也能夠用于識(shí)別給予組合繼承創(chuàng)建的對(duì)象類型。
四、原型式繼承感興趣可以了解一下。
原型鏈中,我們是讓原型對(duì)象指向一個(gè)構(gòu)造函數(shù)的實(shí)例,這個(gè)實(shí)例本質(zhì)上就是一個(gè)對(duì)象。原型式繼承就是讓原型對(duì)象指向一個(gè)已有的對(duì)象,不必創(chuàng)建自定義類型。如下:
function object(o){ ??? function F(){} ??? F.prototype = o; ??? return new F(); } var person = { ??? name: "Nicholas", ??? friends: ["Shelby", "Court", "Van"] }; var anotherPerson = object(person); anotherPerson.name = "Greg"; anotherPerson.friends.push("Rob"); var yetAnotherPerson = object(person); yetAnotherPerson.name = "Linda"; yetAnotherPerson.friends.push("Barbie"); console.log(person.friends);?? //"Shelby,Court,Van,Rob,Barbie”
大家還記得原型模式嗎。我的理解:這就是一個(gè)原型模式,區(qū)別是object這個(gè)方法就相當(dāng)于一個(gè)工廠,你傳給它一個(gè)對(duì)象,它就給你一個(gè)原型是這個(gè)對(duì)象的實(shí)例。這個(gè)實(shí)例就會(huì)相應(yīng)的繼承到了你傳給它的那個(gè)對(duì)象的屬性。
當(dāng)然你也可以不用自己寫上面的object這個(gè)方法,因?yàn)镋S5提供了,而且更規(guī)范。ES5中新增了Object.create()方法規(guī)范化了原型式繼承。這個(gè)方法接受兩個(gè)參數(shù):一個(gè)是用做新對(duì)象原型的對(duì)象和(可選)一個(gè)為新對(duì)象定義額外屬性的對(duì)象(或者說是定義新對(duì)象的實(shí)例屬性的對(duì)象,這個(gè)參數(shù)和defineProperties()方法的第二個(gè)參數(shù)格式相同:每個(gè)屬性都是通過自己的描述符定義的)。
上代碼:
var person = { ??? name: "Nicholas", ??? friends: ["Shelby", "Court", "Van"] }; ?????????????????? var anotherPerson = Object.create(person, { ??? name: { ??????? value: "Greg" ??? } }); console.log(anotherPerson);
打印結(jié)果圖:
從上圖可以看到第二個(gè)參數(shù)定義的name屬性是新對(duì)象的實(shí)例屬性,它會(huì)屏蔽掉它的原型屬性里的同名屬性name。簡(jiǎn)單來說,Object.create就是用原型模式創(chuàng)建新對(duì)象的一個(gè)工廠,第一個(gè)參數(shù)定義了原型屬性,第二個(gè)參數(shù)定義了實(shí)例屬性。
五、寄生式繼承這一小節(jié),感興趣了解一下。
六、寄生組合式繼承前面說過,組合繼承是js里最常用的繼承模式,但是它并不完美。問題是:調(diào)用了兩次超類SuperType的構(gòu)造函數(shù),子類創(chuàng)建了一部分多余的屬性(這部分屬性是超類的實(shí)例屬性,在子類的實(shí)例屬性里存在并有用,但在子類的原型中也存在且沒用)。寄生組合式繼承就是解決這個(gè)問題的。
上代碼:
function object(o){ ??? function F(){} ??? F.prototype = o; ??? return new F(); } function inheritPrototype(subType, superType){ ??? var prototype = object(superType.prototype);?? //create object??? prototype.constructor = subType;?????????//augment object??? subType.prototype = prototype;???????? //assign object } ??????????????????????? function SuperType(name){ ??? this.name = name; ??? this.colors = ["red", "blue", "green"]; } SuperType.prototype.sayName = function(){ ??? alert(this.name); }; function SubType(name, age){? ??? SuperType.call(this, name); ??? this.age = age; } inheritPrototype(SubType, SuperType); SubType.prototype.sayAge = function(){ ??? alert(this.age); }; var instance1 = new SubType("Nicholas", 29); instance1.colors.push("black"); alert(instance1.colors);?//"red,blue,green,black" instance1.sayName();????? //"Nicholas"; instance1.sayAge();?????? //29 var instance2 = new SubType("Greg", 27); alert(instance2.colors);? //"red,blue,green" instance2.sayName();????? //"Greg"; instance2.sayAge();?????? //27 console.log(instance1); console.log(SuperType.prototype)
代碼運(yùn)行打印結(jié)果圖:
從圖中可以看到instance1(子類實(shí)例)的原型里已經(jīng)沒有了超類的實(shí)例屬性name、colors。而且代碼中只運(yùn)行了一次超類構(gòu)造函數(shù)。怎么做到的呢?請(qǐng)看下面的解釋:
我們先看這段代碼:
function inheritPrototype(subType, superType){ ??? var prototype = object(superType.prototype);?? //create object ??? prototype.constructor = subType;?????????????? //augment object ??? subType.prototype = prototype;???????????????? //assign object }
subType的原型還是指向了一個(gè)對(duì)象,這個(gè)對(duì)象是什么呢?object這個(gè)方法返回的對(duì)象,這個(gè)對(duì)象是一個(gè)構(gòu)造函數(shù)是空的,原型指向超類原型的實(shí)例。什么意思呢?就是說subType的原型還是一個(gè)構(gòu)造函數(shù)的實(shí)例,但不是超類SuperType的實(shí)例,而是一個(gè)新建的臨時(shí)的空的構(gòu)造函數(shù)F的實(shí)例。看代碼:
function object(o){ ??? function F(){} ??? F.prototype = o; ??? return new F(); }
這個(gè)臨時(shí)的構(gòu)造函數(shù)F具有和超類SuperType一樣的原型。那么這個(gè)時(shí)候的子類的原型中就只有F的實(shí)例屬性和原型,而F的實(shí)例屬性是空的,就只有F的原型,F(xiàn)的原型就是超類SuperType的原型。這樣子類的實(shí)例屬性還是繼承了超類的實(shí)例屬性,而子類的原型屬性只繼承了超類的原型。完美,就這樣。
啰嗦一句我對(duì)面向?qū)ο蟪绦蛟O(shè)計(jì)的理解,面向?qū)ο蟪绦蛟O(shè)計(jì)就是一直在說如何使用對(duì)象。其實(shí),只要結(jié)果符合你的預(yù)期,對(duì)象真的是想怎么使用就怎么使用,不一定非得像書中說的什么各種模式的。當(dāng)然書中的這么多種模式方法的介紹可以了解一下(但是構(gòu)造函數(shù)模式、原型模式。以及繼承里的原型鏈、借用構(gòu)造函數(shù)。還包括它們的組合使用還是需要認(rèn)真研讀,深刻理解的。再順便說一句,繼承里的原型鏈、借用構(gòu)造函數(shù)可以看作是原型模式和構(gòu)造函數(shù)模式的進(jìn)化),可以加深自己對(duì)對(duì)象的理解,有助于你花式使用對(duì)象的方法。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/89309.html
摘要:把對(duì)象定義為無序?qū)傩缘募希鋵傩钥梢园局祵?duì)象或函數(shù)。接受兩個(gè)參數(shù)屬性所在的對(duì)象和要讀取其特性的屬性名返回的時(shí)其特性的對(duì)象例如讀高級(jí)程序設(shè)計(jì)這本書的第章面向?qū)ο蟮某绦蛟O(shè)計(jì),我做了篇筆記。這是第一篇,后面還有兩篇,分別是封裝類和繼承。 ECMA-262把對(duì)象定義為:無序?qū)傩缘募?,其屬性可以包含基本值、?duì)象或函數(shù)。所以,我們可以理解對(duì)象就是名值對(duì)的集合,名就是對(duì)象的每個(gè)屬性的名字,...
摘要:創(chuàng)建構(gòu)造函數(shù)后,其原型對(duì)象默認(rèn)只會(huì)取得屬性至于其他的方法都是從繼承來的。上圖展示了構(gòu)造函數(shù)的原型對(duì)象和現(xiàn)有的兩個(gè)實(shí)例之間的關(guān)系。所有原生的引用類型都在其構(gòu)造函數(shù)的原型上定義了方法。 第6章我一共寫了3篇總結(jié),下面是相關(guān)鏈接:讀《javaScript高級(jí)程序設(shè)計(jì)-第6章》之理解對(duì)象讀《javaScript高級(jí)程序設(shè)計(jì)-第6章》之繼承 工廠模式 所謂的工廠模式就是,把創(chuàng)建具體對(duì)象的過程抽象...
摘要:題外話最近在看高級(jí)程序設(shè)計(jì)這本書,面對(duì)著多頁的厚書籍,心里有點(diǎn)壓力,所以我決定梳理一下。。全局環(huán)境的關(guān)閉是頁面關(guān)閉或者瀏覽器關(guān)閉,而局部環(huán)境的關(guān)閉是指函數(shù)結(jié)束。數(shù)值范圍最大和最小的范圍是超出范圍的數(shù)字如何表示是一個(gè)特殊的值。 題外話 最近在看《JavaScript高級(jí)程序設(shè)計(jì)》這本書,面對(duì)著700多頁的厚書籍,心里有點(diǎn)壓力,所以我決定梳理一下。。探究一下到底怎么讀這本書。本書的內(nèi)容好像...
摘要:題外話最近在看高級(jí)程序設(shè)計(jì)這本書,面對(duì)著多頁的厚書籍,心里有點(diǎn)壓力,所以我決定梳理一下。。全局環(huán)境的關(guān)閉是頁面關(guān)閉或者瀏覽器關(guān)閉,而局部環(huán)境的關(guān)閉是指函數(shù)結(jié)束。數(shù)值范圍最大和最小的范圍是超出范圍的數(shù)字如何表示是一個(gè)特殊的值。 題外話 最近在看《JavaScript高級(jí)程序設(shè)計(jì)》這本書,面對(duì)著700多頁的厚書籍,心里有點(diǎn)壓力,所以我決定梳理一下。。探究一下到底怎么讀這本書。本書的內(nèi)容好像...
摘要:題外話最近在看高級(jí)程序設(shè)計(jì)這本書,面對(duì)著多頁的厚書籍,心里有點(diǎn)壓力,所以我決定梳理一下。。全局環(huán)境的關(guān)閉是頁面關(guān)閉或者瀏覽器關(guān)閉,而局部環(huán)境的關(guān)閉是指函數(shù)結(jié)束。數(shù)值范圍最大和最小的范圍是超出范圍的數(shù)字如何表示是一個(gè)特殊的值。 題外話 最近在看《JavaScript高級(jí)程序設(shè)計(jì)》這本書,面對(duì)著700多頁的厚書籍,心里有點(diǎn)壓力,所以我決定梳理一下。。探究一下到底怎么讀這本書。本書的內(nèi)容好像...
閱讀 2836·2021-11-19 09:40
閱讀 3695·2021-11-15 18:10
閱讀 3281·2021-11-11 16:55
閱讀 1231·2021-09-28 09:36
閱讀 1647·2021-09-22 15:52
閱讀 3367·2019-08-30 14:06
閱讀 1160·2019-08-29 13:29
閱讀 2307·2019-08-26 17:04