javascript
【深入JavaScript】3.JavaScript继承的实现总结
一、JavaScript對(duì)象
ECMA-262把對(duì)象定義為:“無(wú)序?qū)傩缘募?#xff0c;其屬性可以包含基本值、對(duì)象或者函數(shù)。”
說(shuō)白了就是一組無(wú)序名值對(duì),其中值可以是數(shù)據(jù)或函數(shù)。
?
二、創(chuàng)建對(duì)象的方法總結(jié)
1.創(chuàng)建Object的實(shí)例
(1)方式:創(chuàng)建一個(gè)Object類(lèi)型的實(shí)例,然后再為它添加屬性和方法。
(2)例子:
var person = new Object(); person.name = "kwan"; person.age = 22; person.job = "student";person.sayName = function () {alert(this.name); };(3)優(yōu)點(diǎn):簡(jiǎn)單,容易理解。
(4)缺點(diǎn):使用同一個(gè)接口創(chuàng)建很多對(duì)象,會(huì)產(chǎn)生大量重復(fù)代碼,且無(wú)法識(shí)別對(duì)象類(lèi)型。
(5)總結(jié):這是創(chuàng)建自定義對(duì)象的最簡(jiǎn)單的方式。
?
2.工廠模式
(1)方式:在JS中定義一個(gè)工廠函數(shù)來(lái)實(shí)現(xiàn)這一模式。
(2)例子:
function createPerson(name, age, job) {var o = new Object();o.name = name;o.age = age;o.job = job;o.sayName = function () {alert(this.name);};return o; }?
(3)優(yōu)點(diǎn):減少重復(fù)使用的代碼。
(4)缺點(diǎn):無(wú)法識(shí)別對(duì)象的類(lèi)型。
(5)總結(jié):工廠模式抽象了創(chuàng)建具體對(duì)象的細(xì)節(jié),工廠函數(shù)就像一個(gè)真正的工廠,只需為它傳遞參數(shù),就可以流水線(xiàn)式產(chǎn)生相應(yīng)的對(duì)象。
?
3.構(gòu)造函數(shù)模式
(1)方式:創(chuàng)建自定義的構(gòu)造函數(shù),從而定義自定義對(duì)象類(lèi)型的屬性和方法。
(2)例子:
function Person(name, age, job) {this.name = name;this.age = age;this.job = job;this.sayName = function () {alert(this.name);}; }(3)優(yōu)點(diǎn):可將它的實(shí)例標(biāo)識(shí)為一種特定的類(lèi)型。
(4)缺點(diǎn):每個(gè)方法都要在每個(gè)實(shí)例上重新創(chuàng)建一遍,但其實(shí)方法是可以讓所有實(shí)例共享的。
而如果簡(jiǎn)單地將函數(shù)定義轉(zhuǎn)移到外部,讓對(duì)象引用它,那么該模式又失去封裝性。
(5)總結(jié):
1.構(gòu)造函數(shù)模式中:
a.沒(méi)有顯式創(chuàng)建對(duì)象
b.直接將屬性和方法賦給了this對(duì)象
?? c.也沒(méi)有return語(yǔ)句
d.且函數(shù)名首字母大寫(xiě)(慣例)
2.使用new操作符調(diào)用構(gòu)造函數(shù)創(chuàng)建新實(shí)例的機(jī)制是:
a.創(chuàng)建一個(gè)新對(duì)象
b.將構(gòu)造函數(shù)的作用域賦給新對(duì)象(因此this指向這個(gè)新對(duì)象)
c.執(zhí)行構(gòu)造函數(shù)中的代碼
d.返回新對(duì)象
3.新實(shí)例具有一個(gè)constructor屬性,該屬性指向構(gòu)造函數(shù)。
4.構(gòu)造函數(shù)與其他函數(shù)的唯一區(qū)別,就在于調(diào)用它們的方式不同。調(diào)用構(gòu)造函數(shù)時(shí)需用new操作符。任何函數(shù)通過(guò)new操作符調(diào)用都會(huì)變成構(gòu)造函數(shù),而原本的構(gòu)造函數(shù)若不使用new操作符而是直接調(diào)用,那和普通的函數(shù)沒(méi)什么兩樣,此時(shí)的this指代調(diào)用該函數(shù)的作用域。
4.原型模式
(1)方式:通過(guò)函數(shù)的prototype屬性,將可以共享的信息直接添加到原型對(duì)象中。
(2)例子:
function Person() {}Person.prototype.name = "kwan"; Person.prototype.age = "22"; Person.prototype.job = "student"; Person.prototype.sayName = function () {alert(this.name); };(3)優(yōu)點(diǎn):可讓所有對(duì)象實(shí)例共享其原型對(duì)象所包含的屬性和方法。
(4)缺點(diǎn):首先,它省略了為構(gòu)造函數(shù)傳遞初始化參數(shù)這一環(huán)節(jié),結(jié)果所有實(shí)例在默認(rèn)情況下都將取得相同的屬性值;
? 其次,由于其共享的本性,當(dāng)屬性值為引用類(lèi)型時(shí),會(huì)導(dǎo)致很大問(wèn)題。
(5)總結(jié):
1.我們創(chuàng)建的每個(gè)函數(shù)都有一個(gè)prototype屬性,該屬性值為指針,指向一個(gè)對(duì)象,即原型對(duì)象,它的用途是包含可以由特定類(lèi)型的所有實(shí)例共享的屬性和方法。
2.使用原型模式創(chuàng)建的對(duì)象都在使用同一組屬性和函數(shù),這便是該模式的共享特性。
3.若實(shí)例屬性與原型對(duì)象屬性同名,則優(yōu)先取實(shí)例屬性值(屏蔽)
4.實(shí)例的內(nèi)部屬性__proto__指向原型對(duì)象,這便是兩者之間的連接。繼承的實(shí)現(xiàn),以及對(duì)象屬性的查找,其實(shí)都是依賴(lài)于這個(gè)連接。
? ? 5.讀取對(duì)象屬性的過(guò)程是:先找實(shí)例,后找原型。
6.原型是動(dòng)態(tài)的,因此我們對(duì)原型對(duì)象所做的任何修改都能夠立即從實(shí)例上反映出來(lái)。
?
5.組合使用構(gòu)造函數(shù)模式和原型模式(優(yōu)化方案)
(1)方式:構(gòu)造函數(shù)模式用于定義實(shí)例屬性,而原型模式用于定義方法和共享屬性。
(2)例子:
function Person(name, age, job) {this.name = name;this.age = age;this.job = job;this.friends = ["kwan", "soyi"]; }Person.prototype = {constructor : Person,sayName : function () {alert(this.name);} }(3)優(yōu)點(diǎn):每個(gè)實(shí)例都會(huì)有自己的一份實(shí)例屬性的副本,同時(shí)又共享對(duì)方法和共享屬性的引用,最大限度地節(jié)省了內(nèi)存;另外,該模式支持向構(gòu)造函數(shù)傳遞參數(shù)。
(4)缺點(diǎn):封裝性不足。
(5)總結(jié):挺好用。
?
6.動(dòng)態(tài)原型模式
(1)方式:把所有信息都封裝在了構(gòu)造函數(shù)中,通過(guò)檢查某個(gè)應(yīng)該存在的方法是否有效,來(lái)決定是否需要初始化原型。
(2)例子:
function Person(name, age, job) {this.name = name;this.age = age;this.job = job;this.friends = ["kwan", "soyi"];if (typeof this.sayName != "function") {//這個(gè)判斷是必要的,且只需寫(xiě)一個(gè)條件Person.prototype.sayName = function () {alert(this.name);};} }(3)優(yōu)點(diǎn):僅在必要情況下通過(guò)在構(gòu)造函數(shù)中初始化原型,保持同時(shí)使用構(gòu)造函數(shù)和原型的優(yōu)點(diǎn),封裝度更高。
(4)缺點(diǎn):暫無(wú)。
(5)總結(jié):
通過(guò)if條件的判斷,只有在第一個(gè)實(shí)例被new出來(lái)的時(shí)候才會(huì)為原型對(duì)象添加屬性和方法,接下來(lái)的所有對(duì)象的實(shí)例化均不會(huì)調(diào)用添加操作,而且這樣寫(xiě)可將所有信息封裝在構(gòu)造函數(shù)中。
?
7.寄生構(gòu)造函數(shù)模式(了解)
(1)方式:創(chuàng)建一個(gè)函數(shù),該函數(shù)的作用僅僅是封裝創(chuàng)建對(duì)象的代碼,然后再返回新創(chuàng)建的對(duì)象。
(2)例子:
function Person(name, age, job) {var o = new Ojbect();o.name = name;o.age = age;o.job = job;o.sayName = function () {alert(this.name);};return o; }(3)優(yōu)點(diǎn):暫無(wú)。
(4)缺點(diǎn):返回的對(duì)象與構(gòu)造函數(shù)或構(gòu)造函數(shù)的原型之間沒(méi)有關(guān)系,為此不能依賴(lài)instanceof操作符來(lái)確定對(duì)象類(lèi)型。
(5)總結(jié):
1.構(gòu)造函數(shù)在不返回值的情況下,默認(rèn)會(huì)返回新對(duì)象實(shí)例。而通過(guò)在構(gòu)造函數(shù)的末尾添加一個(gè)return語(yǔ)句,可以重寫(xiě)返回值。
2.這個(gè)模式的應(yīng)用條件是:在特殊情況下用來(lái)為對(duì)象創(chuàng)建構(gòu)造函數(shù)。盡量不要用這個(gè)模式。
?
8.穩(wěn)妥構(gòu)造函數(shù)模式(了解)
(1)方式:穩(wěn)妥構(gòu)造函數(shù)遵循與寄生構(gòu)造函數(shù)類(lèi)似的模式,但區(qū)別是:新創(chuàng)建對(duì)象的實(shí)例方法不引用this,并且不使用new操作符調(diào)用構(gòu)造函數(shù)。
(2)例子:
function Person(name, age, job) {var o = new Ojbect();o.sayName = function () {alert(name);};return o; }(3)優(yōu)點(diǎn):暫無(wú)。
(4)缺點(diǎn):返回的對(duì)象與構(gòu)造函數(shù)或構(gòu)造函數(shù)的原型之間沒(méi)有關(guān)系,為此不能依賴(lài)instanceof操作符來(lái)確定對(duì)象類(lèi)型。
(5)總結(jié):
1.所謂穩(wěn)妥對(duì)象,指的是沒(méi)有公共屬性,而且其方法也不引用this的對(duì)象。
2.穩(wěn)妥對(duì)象最適合在一些安全的環(huán)境中(這些環(huán)境中會(huì)禁止使用this和new),或者在防止數(shù)據(jù)被其他應(yīng)用程序(如Mashup)改動(dòng)時(shí)使用。
?
三、繼承總結(jié)
在ECMAScript中無(wú)法實(shí)現(xiàn)接口繼承,它只支持實(shí)現(xiàn)繼承,而且其實(shí)現(xiàn)繼承主要是依靠原型鏈來(lái)實(shí)現(xiàn)的。
1.原型鏈繼承
(1)什么是原型鏈?
原型鏈的基本思想是:利用原型讓一個(gè)引用類(lèi)型繼承另一個(gè)引用類(lèi)型的屬性和方法。
我們讓原型對(duì)象A等于另一個(gè)類(lèi)型的實(shí)例,此時(shí)的原型A將包含一個(gè)指向另一個(gè)原型B的指針,相應(yīng)地,原型B中也包含著一個(gè)指向另一個(gè)構(gòu)造函數(shù)的指針。假如原型B又是另一個(gè)原型C的實(shí)例,那么上述關(guān)系依然成立,如此層層推進(jìn),就構(gòu)成了實(shí)例與原型的鏈條(A->B->C)。這就是原型鏈的概念。
(2)例子:
function SuperType(){ //父類(lèi)this.property=true; }//在父類(lèi)原型中添加方法 SuperType.prototype.getSuperValue=function(){return this.property; };function SubType(){ //子類(lèi)this.subproperty=false; }//實(shí)現(xiàn)繼承的代碼: SubType.prototype=new SuperType();//在子類(lèi)的新原型中添加方法 SubType.prototype.getSubValue=function(){return this.subproperty; };(3)原型鏈的本質(zhì)是什么?
利用原型鏈實(shí)現(xiàn)繼承的本質(zhì)是重寫(xiě)原型對(duì)象,代之以一個(gè)新類(lèi)型的實(shí)例。
同時(shí),通過(guò)實(shí)現(xiàn)原型鏈,本質(zhì)上擴(kuò)展了原型搜索機(jī)制。
在通過(guò)原型鏈實(shí)現(xiàn)繼承后,搜索屬性或方法的過(guò)程就得以沿著原型鏈繼續(xù)向上。
(4)或許來(lái)一個(gè)圖,會(huì)更加清晰:
(5)原型鏈的問(wèn)題:
1.當(dāng)存在包含引用類(lèi)型值的原型時(shí),某個(gè)實(shí)例對(duì)引用類(lèi)型值的修改,會(huì)反映在其余所有實(shí)例上;
2.在創(chuàng)建子類(lèi)型的實(shí)例時(shí),沒(méi)有辦法在不影響所有對(duì)象實(shí)例的情況下,給超類(lèi)型的構(gòu)造函數(shù)傳遞參數(shù)。
?
2.借用構(gòu)造函數(shù)(偽造對(duì)象、經(jīng)典繼承)
(1)基本思想:在子類(lèi)型構(gòu)造函數(shù)的內(nèi)部調(diào)用超類(lèi)型構(gòu)造函數(shù)。
(2)例子:
function SuperType() { //父類(lèi)this.colors = ["red", "blue", "green"]; }function SubType() {//繼承SuperTypeSuperType.call(this); }(3)優(yōu)點(diǎn):可在子類(lèi)型構(gòu)造函數(shù)中向超類(lèi)型構(gòu)造函數(shù)傳遞參數(shù)。
(4)缺點(diǎn):僅使用此技術(shù)實(shí)現(xiàn)繼承,函數(shù)無(wú)法復(fù)用。而且在超類(lèi)型的原型中定義的方法,對(duì)子類(lèi)型而言也是不可見(jiàn)的。
(5)總結(jié):
僅僅借用構(gòu)造函數(shù)實(shí)現(xiàn)繼承,本質(zhì)上是沒(méi)有利用到原型鏈的。因此很少單獨(dú)使用該模式。
?
3.組合繼承(偽經(jīng)典繼承)
(1)方式:將原型鏈和借用構(gòu)造函數(shù)的技術(shù)組合在一塊。
? 其基本思想是:使用原型鏈實(shí)現(xiàn)對(duì)原型屬性和方法的繼承,通過(guò)借用構(gòu)造函數(shù)來(lái)實(shí)現(xiàn)對(duì)實(shí)例屬性的繼承。
(2)例子:
function SuperType(name) { //父類(lèi)this.name = name;this.colors = ["red", "blue", "green"]; }SuperType.prototype.sayName = function () {alert(this.name); };function SubType(name, age) {//繼承SuperType的實(shí)例屬性SuperType.call(this, name); //第二次調(diào)用超類(lèi)型構(gòu)造函數(shù)this.age = age; }//繼承方法 SubType.prototype = new SuperType(); //第一次調(diào)用超類(lèi)型構(gòu)造函數(shù) SubType.prototype.sayAge = function () {alert(this.age); };(3)優(yōu)點(diǎn):既通過(guò)在原型上定義方法實(shí)現(xiàn)了函數(shù)復(fù)用,又能夠保證每個(gè)實(shí)例都有它自己的屬性。
(4)缺點(diǎn):無(wú)論在什么情況下,都會(huì)調(diào)用兩次超類(lèi)型構(gòu)造函數(shù),以至于重寫(xiě)超類(lèi)型對(duì)象的實(shí)例屬性,造成空間的浪費(fèi)。(這個(gè)自己畫(huà)圖或看書(shū)理解)
(5)總結(jié):最常用的繼承模式。
?
4.寄生組合式繼承
(1)方式:通過(guò)借用構(gòu)造函數(shù)來(lái)繼承屬性,通過(guò)原型鏈的混成形式來(lái)繼承方法。
(2)基本思路:不必為了指定子類(lèi)型的原型而調(diào)用超類(lèi)型的構(gòu)造函數(shù),我們所需要的無(wú)非就是超類(lèi)型原型的一個(gè)副本而已。本質(zhì)上,就是使用寄生式繼承來(lái)繼承超類(lèi)型的原型,然后再將結(jié)果指定給子類(lèi)型的原型。
(3)例子:
首先,寄生組合式繼承的基本模式是:
function inheritPrototype(subType, superType) {var prototype = object(superType.prototype); //創(chuàng)建對(duì)象prototype.constructor = subType; //增強(qiáng)對(duì)象subType.prototype = prototype; //指定對(duì)象 }//原型式繼承 function object(o) {function F() {}F.prototype = o;return new F(); }示例中,object函數(shù)首先生成一個(gè)空白構(gòu)造函數(shù)F,再將傳入的對(duì)象o指定為F的原型對(duì)象,最后返回一個(gè)F的實(shí)例。
這樣一來(lái),返回的對(duì)象的原型就是o,而本身這個(gè)對(duì)象是空白的。
?
而inheritPrototype函數(shù)的作用是:
首先調(diào)用object函數(shù),傳入超類(lèi)型的原型,從而得到一個(gè)F的空白實(shí)例,此時(shí)該實(shí)例的原型就是超類(lèi)型的原型;
然后,將這個(gè)實(shí)例的構(gòu)造函數(shù)指針指定為subType,因此這個(gè)實(shí)例是用作subType的原型的;
最后,將第一步所得的空白實(shí)例對(duì)象指定為subType的原型。
至此,subType的原型不再是superType的實(shí)例,而是一個(gè)空白的實(shí)例,但這個(gè)實(shí)例原型又是suerType。
下面給出示例2:
function SuperType(name) { //父類(lèi)this.name = name;this.colors = ["red", "blue", "green"]; }SuperType.prototype.sayName = function () {alert(this.name); };function SubType(name, age) {//繼承SuperType的實(shí)例屬性SuperType.call(this, name); //僅這一次調(diào)用超類(lèi)型構(gòu)造函數(shù)this.age = age; }inheritPrototype(SubType, SuperType);SubType.prototype.sayAge = function () {alert(this.age); };//繼承方法 SubType.prototype = new SuperType(); //第一次調(diào)用超類(lèi)型構(gòu)造函數(shù) SubType.prototype.sayAge = function () {alert(this.age); };(4)優(yōu)點(diǎn):這個(gè)例子的高效率體現(xiàn)在它只調(diào)用一次SuperType的構(gòu)造函數(shù),并且因此避免了在SubType.prototype上創(chuàng)建不必要的、多余的屬性。與此同時(shí),原型鏈還能保持不變。
?
5.原型式繼承(了解)
(1)方式:借助原型可以基于已有的對(duì)象創(chuàng)建新對(duì)象,同時(shí)還不必因此創(chuàng)建自定義類(lèi)型。
(2)例子:
//原型式繼承 function object(o) {function F() {}F.prototype = o;return new F(); }(3)本質(zhì):object()對(duì)傳入其中的對(duì)象執(zhí)行了一次淺復(fù)制。
(4)應(yīng)用條件:這種繼承模式要求必須有一個(gè)對(duì)象可以作為另一個(gè)對(duì)象的基礎(chǔ)。如果有這么一個(gè)對(duì)象的話(huà),可以把它傳遞給object()函數(shù),然后再根據(jù)具體需求對(duì)得到的對(duì)象加以修改即可。
?
6.寄生式繼承(了解)
(1)思路:與寄生構(gòu)造函數(shù)和工廠模式類(lèi)似,即創(chuàng)建一個(gè)僅用于封裝繼承過(guò)程的函數(shù),該函數(shù)在內(nèi)部以某種方式來(lái)增強(qiáng)對(duì)象,最后再像是真的是它做了所有工作一樣返回對(duì)象。
(2)例子:
function createAnother(original) {var clone = object(original); //通過(guò)調(diào)用函數(shù)創(chuàng)建一個(gè)新對(duì)象clone.sayHi = function () {alert("hi");};//以某種方式來(lái)增強(qiáng)這個(gè)對(duì)象return clone; }?
四、總結(jié)
這一篇總結(jié)寫(xiě)到我吐血...估計(jì)看的人也吐血了吧...已經(jīng)盡可能簡(jiǎn)單化了,希望各位喜歡。
共勉。
?
轉(zhuǎn)載于:https://www.cnblogs.com/stay-foolish/archive/2013/04/04/2482813.html
總結(jié)
以上是生活随笔為你收集整理的【深入JavaScript】3.JavaScript继承的实现总结的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Thrift简析
- 下一篇: SpringMVC 实例应用 -- 不同