本篇文章為大家展示了javascript中有哪些繼承形式,內(nèi)容簡明扼要并且容易理解,絕對能使你眼前一亮,通過這篇文章的詳細(xì)介紹希望你能有所收獲。
成都創(chuàng)新互聯(lián)公司服務(wù)項目包括興寧網(wǎng)站建設(shè)、興寧網(wǎng)站制作、興寧網(wǎng)頁制作以及興寧網(wǎng)絡(luò)營銷策劃等。多年來,我們專注于互聯(lián)網(wǎng)行業(yè),利用自身積累的技術(shù)優(yōu)勢、行業(yè)經(jīng)驗、深度合作伙伴關(guān)系等,向廣大中小型企業(yè)、政府機構(gòu)等提供互聯(lián)網(wǎng)行業(yè)的解決方案,興寧網(wǎng)站推廣取得了明顯的社會效益與經(jīng)濟效益。目前,我們服務(wù)的客戶以成都為中心已經(jīng)輻射到興寧省份的部分城市,未來相信會繼續(xù)擴大服務(wù)區(qū)域并繼續(xù)獲得客戶的支持與信任!
Js的繼承大體上就是兩種:對象冒充、原型方式。這兩種方式各有優(yōu)點和缺陷,這里我先列舉出來,再從底層分析區(qū)別:
(一)對象冒充
function A(name){ this.name = name; this.sayHello = function(){alert(this.name+” say Hello!”);}; } function B(name,id){ this.temp = A; this.temp(name); //相當(dāng)于new A(); delete this.temp; //防止在以后通過temp引用覆蓋超類A的屬性和方法 this.id = id; this.checkId = function(ID){alert(this.id==ID)}; }
當(dāng)構(gòu)造對象B的時候,調(diào)用temp相當(dāng)于啟動A的構(gòu)造函數(shù),注意這里的上下文環(huán)境中的this對象是B的實例,所以在執(zhí)行A構(gòu)造函數(shù)腳本時,所有A的變量 和方法都會賦值給this所指的對象,即B的實例,這樣子就達(dá)到B繼承了A的屬性方法的目的。
之后刪除臨時引用temp,是防止維護(hù)B中對A的類對象(注 意不是實例對象)的引用更改,因為更改temp會直接導(dǎo)致類A(注意不是類A的對象)結(jié)構(gòu)的變化。
我們看到了,在Js版本更新的過程中,為了更方便的執(zhí)行這種上下文this的切換以達(dá)到繼承或者更加廣義的目的,增加了call和apply函數(shù)。它們的 原理是一樣的,只是參數(shù)不同的版本罷了(一個可變?nèi)我鈪?shù),一個必須傳入數(shù)組作為參數(shù)集合)。這里就以call為例子,解釋一下用call實現(xiàn)的對象冒充 繼承。
function Rect(width, height){ this.width = width; this.height = height; this.area = function(){return this.width*this.height;}; } function myRect(width, height, name){ Rect .call(this,width,height); this.name = name; this.show = function(){ alert(this.name+” with area:”+this.area()); } }
關(guān)于Call方法,官方解釋:調(diào)用一個對象的一個方法,以另一個對象替換當(dāng)前對象。
call (thisOb,arg1, arg2…)
這也是一種對象冒充的繼承,其實在call方法調(diào)用的時候發(fā)生的事情也是上下文環(huán)境變量this的替換,在myRect函數(shù)體中this肯定是指向類 myRect對象的實例了,然而用這個this作為上下文環(huán)境變量調(diào)用名字叫Rect方法,即類Rect的構(gòu)造函數(shù)。
于是此時調(diào)用Rect時候?qū)his 的賦值屬性和方法都實際上是對一個myRect的對象進(jìn)行。所以說盡管call和apply并不是僅僅為了繼承而新增的方法,但用它們可以模擬繼承。
對象冒充繼承就是這么一回事,它可以實現(xiàn)多重繼承,只要重復(fù)做這一套賦值的流程就可以了。不過目前真正大規(guī)模使用得并不多,為什么呢?
因為它有一個明顯的 性能缺陷,這就要說道OO的概念了,我們說對象是成員+成員方法的集合,構(gòu)造對象實例的時候,這些實例只需要擁有各自的成員變量就可以了,成員方法只是一 段對變量操作的可執(zhí)行文本區(qū)域而已,這段區(qū)域不用為每個實例而復(fù)制一份,所有的實例都可以共享。
現(xiàn)在回到Js利用對象冒充模擬的繼承里,所有的成員方法都 是針對this而創(chuàng)建的,也就是所所有的實例都會擁有一份成員方法的副本,這是對內(nèi)存資源的一種極度浪費。
其它的缺陷比如說對象冒充無法繼承 prototype域的變量和方法就不用提了,筆者認(rèn)為前一個致命缺陷就已經(jīng)足夠。不過,我們還是需要理解它,特別是父類的屬性和方法是如何繼承下來的原 理,對于理解Js繼承很重要。
【推薦學(xué)習(xí):javascript高級教程】
(二)原型方式
第二種繼承方式是原型方式,所謂原型方式的繼承,是指利用了prototype或者說以某種方式覆蓋了prototype,從而達(dá)到屬性方法復(fù)制的目的。 其實現(xiàn)方式有很多中,可能不同框架多少會有一點區(qū)別,但是我們把握住原理,就不會有任何不理解的地方了??匆粋€例子(某一種實現(xiàn)):
function Person(){ this.name = “Mike”; this.sayGoodbye = function(){alert(“GoodBye!”);}; } Person.prototype.sayHello = function(){alert(”Hello!”);}; function Student(){} Student.prototype = new Person();
關(guān)鍵是對最后一句Student原型屬性賦值為Person類構(gòu)造的對象,這里筆者解釋一下父類的屬性和方法是如何copy到子類上的。
Js對象在讀取某個對象屬性的時候,總是先查看自身域的屬性列表,如果有就返回否則去讀取prototype域,如果找到就返回,由于prototype可以指向別的對象,所以Js解釋器會遞歸的去查找prototype域指向?qū)ο蟮膒rototype域,直到prototype為本身就停止,此時還沒找到就成undefined了。
這樣看來,最后一句發(fā)生的效果就是將父類所有屬性和方法連接到子類的prototype域上,這樣子類就繼承了父類所有的屬性和方法,包括name、 sayGoodbye和sayHello。這里與其把最后一句看成一種賦值,不如理解成一種指向關(guān)系更好一點。
這種原型繼承的缺陷也相當(dāng)明顯,就是繼承時 父類的構(gòu)造函數(shù)時不能帶參數(shù),因為對子類prototype域的修改是在聲明子類對象之后才能進(jìn)行,用子類構(gòu)造函數(shù)的參數(shù)去初始化父類屬性是無法實現(xiàn)的, 如下所示:
function Person(name){ this.name = name; } function Student(name,id){ this.id = id; } Student.prototype = new Person(this.name);
兩種繼承方式已經(jīng)講完了,如果我們理解了兩種方式下子類如何把父類的屬性和方法“抓取”下來,就可以自由組合各自的利弊,來實現(xiàn)真正合理的Js繼承。下面是個人總結(jié)的一種綜合方式:
function Person(name){ this.name = name; } Person.prototype.sayHello = function(){alert(this.name+“say Hello!”);}; function Student(name,id){ Person.call(this,name); this.id = id; } Student.prototype = new Person(); Student.prototype.show = function(){ alert(“Name is:”+ this.name+” and Id is:”+this.id);
上述內(nèi)容就是javascript中有哪些繼承形式,你們學(xué)到知識或技能了嗎?如果還想學(xué)到更多技能或者豐富自己的知識儲備,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。