麻豆小视频在线观看_中文黄色一级片_久久久成人精品_成片免费观看视频大全_午夜精品久久久久久久99热浪潮_成人一区二区三区四区

首頁(yè) > 編程 > JavaScript > 正文

Javascript中類(lèi)式繼承和原型式繼承的實(shí)現(xiàn)方法和區(qū)別之處

2019-11-19 16:44:50
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友

在所有面向?qū)ο蟮木幊讨校^承是一個(gè)重要的話題。一般說(shuō)來(lái),在設(shè)計(jì)類(lèi)的時(shí)候,我們希望能減少重復(fù)性的代碼,并且盡量弱化對(duì)象間的耦合(讓一個(gè)類(lèi)繼承另一個(gè)類(lèi)可能會(huì)導(dǎo)致二者產(chǎn)生強(qiáng)耦合)。關(guān)于“解耦”是程序設(shè)計(jì)中另一個(gè)重要的話題,本篇重點(diǎn)來(lái)看看在javascript如何實(shí)現(xiàn)繼承。

其它的面向?qū)ο蟪绦蛟O(shè)計(jì)語(yǔ)言都是通過(guò)關(guān)鍵字來(lái)解決繼承的問(wèn)題(比如extend或inherit等方式)。但是javascript中并沒(méi)有定義這種實(shí)現(xiàn)的機(jī)制,如果一個(gè)類(lèi)需要繼承另一個(gè)類(lèi),這個(gè)繼承過(guò)程需要程序員自己通過(guò)編碼來(lái)實(shí)現(xiàn)。

一、類(lèi)式繼承的實(shí)現(xiàn)

1、創(chuàng)建一個(gè)類(lèi)的方式:

//定義類(lèi)的構(gòu)造函數(shù)function Person(name) {  this.name = name || '默認(rèn)姓名';}//定義該類(lèi)所有實(shí)例的公共方法Person.prototype.getName = function() {  return this.name;}var smith = new Person('Smith');var jacky = new Person('Jacky');console.log( smith.getName(), jacky.getName() ); //Smith Jacky

2、繼承這個(gè)類(lèi):

這需要分兩個(gè)步驟來(lái)實(shí)現(xiàn),第1步是繼承父類(lèi)構(gòu)造函數(shù)中定義的屬性,第2步是繼承父類(lèi)的prototype屬性

//定義類(lèi)的構(gòu)造函數(shù)function Person(name) {  this.name = name || '默認(rèn)姓名';}//定義該類(lèi)所有實(shí)例的公共方法Person.prototype.getName = function() {  return this.name;}function Author(name, books) {  //繼承父類(lèi)構(gòu)造函數(shù)中定義的屬性  //通過(guò)改變父類(lèi)構(gòu)造函數(shù)的執(zhí)行上下文來(lái)繼承  Person.call(this, name);  this.books = books;}//繼承父類(lèi)對(duì)應(yīng)的方法Author.prototype = new Person(); //Author.prototype.constructor === PersonAuthor.prototype.constructor = Author; //修正修改原型鏈時(shí)造成的constructor丟失Author.prototype.getBooks = function() {  return this.books;};//測(cè)試var smith = new Person('Smith');var jacky = new Author('Jacky', ['BookA', 'BookB']);console.log(smith.getName()); //Smithconsole.log(jacky.getName()); //Jackyconsole.log(jacky.getBooks().join(', ')); //BookA, BookBconsole.log(smith.getBooks().join(', ')); //Uncaught TypeError: smith.getBooks is not a function

從測(cè)試的結(jié)果中可以看出,Author正確繼承了Person,而且修改Author的原型時(shí),并不會(huì)對(duì)Person產(chǎn)生影響。這其中的關(guān)鍵一句就是 Author.prototype = new Person(),要與Author.prototype = Person.prototype區(qū)分開(kāi)來(lái)。前者產(chǎn)生了一個(gè)實(shí)例,這個(gè)實(shí)例有Person.prototype的副本(這里先這么理解,后面有更詳細(xì)的解析)。后者是指將兩者的prototype指向同一個(gè)原型對(duì)象。

那么,這也意味著每次繼承都將產(chǎn)生一個(gè)父類(lèi)的副本,肯定對(duì)內(nèi)存產(chǎn)生消耗,但為了類(lèi)式繼承這個(gè)內(nèi)存開(kāi)銷(xiāo)必須得支付,但還可以做得更節(jié)省一點(diǎn):Author.prototype = new Person()這一句其實(shí)多執(zhí)行了構(gòu)造函數(shù)一次(而這一次其實(shí)只需在子類(lèi)構(gòu)造函數(shù)中執(zhí)行即可),尤其是在父類(lèi)的構(gòu)造函數(shù)很龐大時(shí)很耗時(shí)和內(nèi)存。修改一下繼承的方式,如下:

Author.prototype = (function() {  function F() {}  F.prototype = Person.prototype;  return new F();})();

如上所示的代碼,new時(shí),去掉了對(duì)父類(lèi)的構(gòu)造函數(shù)的調(diào)用,節(jié)省了一次調(diào)用的開(kāi)銷(xiāo)。

3、類(lèi)式繼承顯著的特點(diǎn)是每一次實(shí)例化對(duì)象時(shí),子類(lèi)都將執(zhí)行一次父類(lèi)的構(gòu)造函數(shù)。如果E繼承了D,D繼承了C,C繼承了B,B繼承了A,在實(shí)例化一個(gè)E時(shí),一共要經(jīng)過(guò)幾次構(gòu)造函數(shù)的調(diào)用呢?

/*繼承方法的函數(shù)*/function extend(son, father) {  function F() {}  F.prototype = father.prototype;  son.prototype = new F();  son.prototype.constructor = son;}//A類(lèi)function A() {  console.log('A()');}A.prototype.hello = function() {  console.log('Hello, world.');}//B類(lèi)function B() {  A.call(this);  console.log('B()');}extend(B, A);//C類(lèi)function C() {  B.call(this);  console.log('C()');}extend(C, B);//D類(lèi)function D() {  C.call(this);  console.log('D()');}extend(D, C);//E類(lèi)function E() {  D.call(this);  console.log('E()');}extend(E, D);//創(chuàng)建一個(gè)E的實(shí)例var e = new E(); //A() B() C() D() E()e.hello(); //hello, world.

5次,這還只是實(shí)例化一個(gè)E時(shí)調(diào)用的次數(shù)。所以,我們應(yīng)該盡可能的減少繼承的級(jí)別。但這并不是說(shuō)不要使用這種類(lèi)式繼承,而是應(yīng)該根據(jù)自己的應(yīng)用場(chǎng)合決定采用什么方法。

二、原型式繼承

1、先來(lái)看一段代碼:我們先將之前類(lèi)式繼承中的繼承prototype那一段改成另一個(gè)函數(shù)clone,然后通過(guò)字面量創(chuàng)建一個(gè)Person,最后讓Author變成Person的克隆體。

//這個(gè)函數(shù)可以理解為克隆一個(gè)對(duì)象function clone(object) {  function F() {}  F.prototype = object;  return new F();}var Person = {  name: 'Default Name';  getName: function() {    return this.name;  }}//接下來(lái)讓Author變?yōu)镻erson的克隆體var Author = clone(Person);

問(wèn)一個(gè)問(wèn)題:clone函數(shù)里的new F()為這個(gè)實(shí)例開(kāi)辟內(nèi)存空間來(lái)存儲(chǔ)object的副本了嗎?

按我之前的理解,回答是肯定的。但是,當(dāng)我繼續(xù)將代碼寫(xiě)下去的時(shí)候,奇怪的事情發(fā)生了,代碼如下:

//接下來(lái)讓Author變?yōu)镻erson的克隆體var Author = clone(Person);Author.books = [];Author.getBooks = function() {  return this.books.join(', ');}//增加一個(gè)作者Smithvar Smith = clone(Author);console.log(Smith.getName(), Smith.getBooks()); //Default NameSmith.name = 'Smith';Smith.books.push('<<Book A>>', '<<Book B>>'); //作者寫(xiě)了兩本書(shū)console.log(Smith.getName(), Smith.getBooks()); //Smith <<Book A>>, <<Book B>>//再增加一個(gè)作者Jackyvar Jacky = clone(Author);console.log(Jacky.getName(), Jacky.getBooks()); // Default Name <<Book A>>, <<Book B>>

當(dāng)我們繼續(xù)增加作者Jacky時(shí),奇怪的現(xiàn)象發(fā)生了!!Jacky的名字依然是Default Name,但是他居然也寫(xiě)兩本與Smith一樣的書(shū)?Jacky的書(shū)都還沒(méi)push呢。到了這里,我想到了引用對(duì)象的情況(引用一個(gè)對(duì)象時(shí),引用的是該對(duì)象的內(nèi)存地址),發(fā)生這樣的現(xiàn)象,問(wèn)題肯定出在clone()函數(shù)中的new F()這里。

事實(shí)上,這個(gè)clone中的new F()確實(shí)返回了一個(gè)新的對(duì)象,該對(duì)象擁有被克隆對(duì)象的所有屬性。但這些屬性保留的是對(duì)被克隆對(duì)象中相應(yīng)屬性的引用,而非一個(gè)完全獨(dú)立的屬性副本。換句話說(shuō),新對(duì)象的屬性 與 被克隆的對(duì)象的屬性指向同一個(gè)內(nèi)存地址(學(xué)過(guò)C語(yǔ)言的同學(xué)應(yīng)該明白指針類(lèi)型,這里意義差不多)。

那么為什么上面的代碼中,Jacky的書(shū)與Smith的書(shū)相同了,為什么Jacky的名字卻不是Smith而是Default Name呢?這就是Javascript中繼承的機(jī)制所在,當(dāng)Smith剛剛繼承自Author時(shí),他的屬性保留了對(duì)Author的屬性的引用,一旦我們顯示的對(duì)Smith的屬性重新賦值時(shí),Javascritp引擎就會(huì)給Smith的該屬性重新劃分內(nèi)存空間來(lái)存儲(chǔ)相應(yīng)的值,由于重新劃分了內(nèi)址地址,那么對(duì)Smith.name的改寫(xiě)就不會(huì)影響到Author.name去了。這就很好的解釋了前面的那個(gè)問(wèn)題――為什么Jacky的名字卻不是Smith而是Default Name。

2、基于原型繼承

通過(guò)前面的情況分析,可以看出基于原型繼承的方式更能節(jié)約內(nèi)存(只有在需要時(shí)候才開(kāi)辟新的內(nèi)存空間)。但要注意:基于原型繼承時(shí),對(duì)象的屬性一定要重新賦值后(重新劃分內(nèi)存)再去引用該屬性。對(duì)于對(duì)象的方法,如果有不同的處理方式,我們只需重新定義即可。

下面將前一段代碼做一個(gè)完整、正確的范例出來(lái),以說(shuō)明原型繼承的特點(diǎn)和使用方式:

//這個(gè)函數(shù)可以理解為克隆一個(gè)對(duì)象function clone(object) {  function F() {}  F.prototype = object;  return new F();}var Person = {  name: 'Default Name',  getName: function() {    return this.name;  }}//接下來(lái)讓Author變?yōu)镻erson的克隆體var Author = clone(Person);Author.books = [];Author.getBooks = function() {  return this.books.join(', ');}//增加一個(gè)作者Smithvar Smith = clone(Author);Smith.name = 'Smith';Smith.books = [];Smith.books.push('<<Book A>>', '<<Book B>>'); //作者寫(xiě)了兩本書(shū)console.log(Smith.getName(), Smith.getBooks()); //Smith <<Book A>>, <<Book B>>//再增加一個(gè)作者Jackyvar Jacky = clone(Author);Jacky.name = 'Jacky';Jacky.books = [];Jacky.books.push('<<Book C>>', '<<Book D>>');console.log(Jacky.getName(), Jacky.getBooks()); // Jacky <<Book C>>, <<Book D>>

三、類(lèi)式繼承與原型式繼承的區(qū)別與相式之處

1、類(lèi)式繼承中:使用構(gòu)造函數(shù)初始化對(duì)象的屬性,通過(guò)調(diào)用父類(lèi)的構(gòu)造函數(shù)來(lái)繼承這些屬性。通過(guò)new 父類(lèi)的prototype來(lái)繼承方法。

2、原型式繼承中:去掉了構(gòu)造函數(shù),但需要將對(duì)象的屬性和方法寫(xiě)一個(gè){}里申明。準(zhǔn)確的說(shuō),原型式繼承就是類(lèi)式繼承中繼承父類(lèi)的prototype方法。

以上所述是小編給大家介紹的Javascript中類(lèi)式繼承和原型式繼承的實(shí)現(xiàn)方法和區(qū)別,希望對(duì)大家有所幫助,如果大家有任何疑問(wèn)請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)武林網(wǎng)網(wǎng)站的支持!

發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 一级免费黄视频 | 欧美日韩精品一区二区三区不卡 | 午夜精品一区二区三区免费 | 羞羞色院91精品网站 | 在线播放一区二区三区 | 青青草好吊色 | 欧美中文字幕一区二区三区亚洲 | 一级做人爱c黑人影片 | 国产精品久久久久久久av | 午夜男人在线观看 | 久久精品一二三区白丝高潮 | 多人乱大交xxxxx变态 | 久久精品久久精品久久精品 | 手机国产乱子伦精品视频 | 91精品国啪老师啪 | 国产高潮好爽受不了了夜色 | 亚洲第一综合色 | bt 自拍 另类 综合 欧美 | 49vvv| 久久经典国产视频 | 911精品影院在线观看 | 宅男噜噜噜66国产免费观看 | 久久蜜桃香蕉精品一区二区三区 | 91av99| 久久久午夜电影 | 欧美性猛交xxx乱大交3蜜桃 | av噜噜在线 | 中文字幕网站在线 | 毛片视频播放 | 热99在线| 国产精品福利一区 | 国产精品久久久久久久久久久久午夜 | 免费a级网站 | 竹内纱里奈和大战黑人 | 国产亚洲小视频 | 伊人一二三四区 | wwwxxx视频| 999精品国产| 黄色成人在线播放 | lutube成人福利在线观看 | 午夜精品成人一区二区 |