亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > JavaScript > 正文

Javascript中類式繼承和原型式繼承的實現方法和區別之處

2019-11-19 16:44:50
字體:
來源:轉載
供稿:網友

在所有面向對象的編程中,繼承是一個重要的話題。一般說來,在設計類的時候,我們希望能減少重復性的代碼,并且盡量弱化對象間的耦合(讓一個類繼承另一個類可能會導致二者產生強耦合)。關于“解耦”是程序設計中另一個重要的話題,本篇重點來看看在javascript如何實現繼承。

其它的面向對象程序設計語言都是通過關鍵字來解決繼承的問題(比如extend或inherit等方式)。但是javascript中并沒有定義這種實現的機制,如果一個類需要繼承另一個類,這個繼承過程需要程序員自己通過編碼來實現。

一、類式繼承的實現

1、創建一個類的方式:

//定義類的構造函數function Person(name) {  this.name = name || '默認姓名';}//定義該類所有實例的公共方法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、繼承這個類:

這需要分兩個步驟來實現,第1步是繼承父類構造函數中定義的屬性,第2步是繼承父類的prototype屬性

//定義類的構造函數function Person(name) {  this.name = name || '默認姓名';}//定義該類所有實例的公共方法Person.prototype.getName = function() {  return this.name;}function Author(name, books) {  //繼承父類構造函數中定義的屬性  //通過改變父類構造函數的執行上下文來繼承  Person.call(this, name);  this.books = books;}//繼承父類對應的方法Author.prototype = new Person(); //Author.prototype.constructor === PersonAuthor.prototype.constructor = Author; //修正修改原型鏈時造成的constructor丟失Author.prototype.getBooks = function() {  return this.books;};//測試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

從測試的結果中可以看出,Author正確繼承了Person,而且修改Author的原型時,并不會對Person產生影響。這其中的關鍵一句就是 Author.prototype = new Person(),要與Author.prototype = Person.prototype區分開來。前者產生了一個實例,這個實例有Person.prototype的副本(這里先這么理解,后面有更詳細的解析)。后者是指將兩者的prototype指向同一個原型對象。

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

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

如上所示的代碼,new時,去掉了對父類的構造函數的調用,節省了一次調用的開銷。

3、類式繼承顯著的特點是每一次實例化對象時,子類都將執行一次父類的構造函數。如果E繼承了D,D繼承了C,C繼承了B,B繼承了A,在實例化一個E時,一共要經過幾次構造函數的調用呢?

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

5次,這還只是實例化一個E時調用的次數。所以,我們應該盡可能的減少繼承的級別。但這并不是說不要使用這種類式繼承,而是應該根據自己的應用場合決定采用什么方法。

二、原型式繼承

1、先來看一段代碼:我們先將之前類式繼承中的繼承prototype那一段改成另一個函數clone,然后通過字面量創建一個Person,最后讓Author變成Person的克隆體。

//這個函數可以理解為克隆一個對象function clone(object) {  function F() {}  F.prototype = object;  return new F();}var Person = {  name: 'Default Name';  getName: function() {    return this.name;  }}//接下來讓Author變為Person的克隆體var Author = clone(Person);

問一個問題:clone函數里的new F()為這個實例開辟內存空間來存儲object的副本了嗎?

按我之前的理解,回答是肯定的。但是,當我繼續將代碼寫下去的時候,奇怪的事情發生了,代碼如下:

//接下來讓Author變為Person的克隆體var Author = clone(Person);Author.books = [];Author.getBooks = function() {  return this.books.join(', ');}//增加一個作者Smithvar Smith = clone(Author);console.log(Smith.getName(), Smith.getBooks()); //Default NameSmith.name = 'Smith';Smith.books.push('<<Book A>>', '<<Book B>>'); //作者寫了兩本書console.log(Smith.getName(), Smith.getBooks()); //Smith <<Book A>>, <<Book B>>//再增加一個作者Jackyvar Jacky = clone(Author);console.log(Jacky.getName(), Jacky.getBooks()); // Default Name <<Book A>>, <<Book B>>

當我們繼續增加作者Jacky時,奇怪的現象發生了??!Jacky的名字依然是Default Name,但是他居然也寫兩本與Smith一樣的書?Jacky的書都還沒push呢。到了這里,我想到了引用對象的情況(引用一個對象時,引用的是該對象的內存地址),發生這樣的現象,問題肯定出在clone()函數中的new F()這里。

事實上,這個clone中的new F()確實返回了一個新的對象,該對象擁有被克隆對象的所有屬性。但這些屬性保留的是對被克隆對象中相應屬性的引用,而非一個完全獨立的屬性副本。換句話說,新對象的屬性 與 被克隆的對象的屬性指向同一個內存地址(學過C語言的同學應該明白指針類型,這里意義差不多)。

那么為什么上面的代碼中,Jacky的書與Smith的書相同了,為什么Jacky的名字卻不是Smith而是Default Name呢?這就是Javascript中繼承的機制所在,當Smith剛剛繼承自Author時,他的屬性保留了對Author的屬性的引用,一旦我們顯示的對Smith的屬性重新賦值時,Javascritp引擎就會給Smith的該屬性重新劃分內存空間來存儲相應的值,由于重新劃分了內址地址,那么對Smith.name的改寫就不會影響到Author.name去了。這就很好的解釋了前面的那個問題――為什么Jacky的名字卻不是Smith而是Default Name。

2、基于原型繼承

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

下面將前一段代碼做一個完整、正確的范例出來,以說明原型繼承的特點和使用方式:

//這個函數可以理解為克隆一個對象function clone(object) {  function F() {}  F.prototype = object;  return new F();}var Person = {  name: 'Default Name',  getName: function() {    return this.name;  }}//接下來讓Author變為Person的克隆體var Author = clone(Person);Author.books = [];Author.getBooks = function() {  return this.books.join(', ');}//增加一個作者Smithvar Smith = clone(Author);Smith.name = 'Smith';Smith.books = [];Smith.books.push('<<Book A>>', '<<Book B>>'); //作者寫了兩本書console.log(Smith.getName(), Smith.getBooks()); //Smith <<Book A>>, <<Book B>>//再增加一個作者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>>

三、類式繼承與原型式繼承的區別與相式之處

1、類式繼承中:使用構造函數初始化對象的屬性,通過調用父類的構造函數來繼承這些屬性。通過new 父類的prototype來繼承方法。

2、原型式繼承中:去掉了構造函數,但需要將對象的屬性和方法寫一個{}里申明。準確的說,原型式繼承就是類式繼承中繼承父類的prototype方法。

以上所述是小編給大家介紹的Javascript中類式繼承和原型式繼承的實現方法和區別,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對武林網網站的支持!

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
91免费欧美精品| 国产精品日本精品| 久久69精品久久久久久国产越南| 亚洲视频在线免费观看| 日韩美女在线观看| 亚洲国产精品电影| www.日韩欧美| 国产精品丝袜高跟| 91禁外国网站| 精品日韩中文字幕| 亚洲精品综合久久中文字幕| 美女久久久久久久| 尤物99国产成人精品视频| 91国内揄拍国内精品对白| 欧美成人免费视频| 亚洲国产精品yw在线观看| 欧美中文字幕在线播放| 国产一区二区在线免费视频| 操91在线视频| 成人国产精品av| 欧美高清videos高潮hd| 国产97人人超碰caoprom| 庆余年2免费日韩剧观看大牛| 亚洲精选一区二区| 国产精品一区二区3区| 91免费在线视频| 亚洲精品影视在线观看| 欧美老女人bb| 69精品小视频| 5278欧美一区二区三区| 国产精品综合不卡av| 欧美一区视频在线| 久久亚洲精品网站| 亚洲精品美女在线观看播放| 91久久久久久久久| 欧洲日韩成人av| 久久久精品国产一区二区| 不卡毛片在线看| 欧美电影在线观看网站| 久久久久女教师免费一区| 日韩在线免费视频| 欧美亚洲国产视频| 欧美性猛交xxxx久久久| 亚洲精品中文字幕女同| 欧美老少做受xxxx高潮| 黑人巨大精品欧美一区免费视频| 97视频免费在线观看| 亚洲va码欧洲m码| 欧美日韩中文字幕日韩欧美| 91久久精品日日躁夜夜躁国产| 亚洲精品欧美日韩专区| 亚洲国产成人精品久久| 久久韩国免费视频| 日韩亚洲第一页| 久久99精品视频一区97| 国产精品三级网站| 国产精品自拍偷拍| 91国内揄拍国内精品对白| 欧美激情一级精品国产| 欧美亚洲一级片| 亚洲欧美国产视频| 免费97视频在线精品国自产拍| 色综合天天狠天天透天天伊人| 精品偷拍各种wc美女嘘嘘| 操人视频在线观看欧美| 欧美亚洲国产另类| 性视频1819p久久| 久久久久久亚洲精品中文字幕| 成人精品视频久久久久| 日韩欧亚中文在线| 国产va免费精品高清在线观看| 国产精品视频久久久| 亚洲va国产va天堂va久久| 欧美大码xxxx| 国产精品偷伦一区二区| 久久久久久久久国产精品| 国产精品美女主播在线观看纯欲| 亚洲综合成人婷婷小说| 亚洲三级免费看| 久久成人精品电影| 91久久精品国产91久久性色| 一区二区av在线| 久久免费在线观看| 精品久久久久久久久久久| 成人性生交大片免费看小说| 久久久久久久久网站| 成人免费视频xnxx.com| 欧美老少配视频| 日韩精品在线播放| 午夜精品一区二区三区在线| 少妇精69xxtheporn| www日韩中文字幕在线看| 亚洲一区二区在线| 久久久精品在线| 欧美高清第一页| 成人精品aaaa网站| 日韩中文字幕在线播放| 国产精品激情av电影在线观看| 国产欧美一区二区三区久久人妖| 欧美性69xxxx肥| 日韩美女主播视频| 黄色精品一区二区| 国产亚洲精品成人av久久ww| 欧美日韩第一页| 奇米一区二区三区四区久久| 亚洲精品一区在线观看香蕉| 992tv成人免费视频| 国产成人久久久精品一区| 欧美日韩色婷婷| 一本色道久久综合狠狠躁篇的优点| 丝袜情趣国产精品| 蜜臀久久99精品久久久无需会员| 久久久91精品国产| 欧美午夜xxx| 久久久精品久久久| 亚洲国产精品热久久| 中文字幕日韩视频| 国产精品吹潮在线观看| 川上优av一区二区线观看| 欧美中文在线字幕| 久久夜色撩人精品| 91精品美女在线| 欧美激情a在线| 日本sm极度另类视频| 亚洲成年人在线| 亚洲精品国产综合久久| 久久韩国免费视频| 色综合久久中文字幕综合网小说| 久久久久久久久久久免费精品| 亚洲午夜久久久久久久| 亚洲精品国产品国语在线| 欧美另类在线观看| 欧美最猛性xxxxx亚洲精品| 亚洲国产精品99久久| 亚洲区中文字幕| 久久精品久久精品亚洲人| 国产欧美精品久久久| 国产精品青草久久久久福利99| 伊人久久久久久久久久久| 亚洲国产精品电影| 97精品一区二区视频在线观看| 国产成人精品午夜| 欧美日韩免费一区| 97香蕉超级碰碰久久免费的优势| 日韩精品免费观看| 日韩成人在线电影网| 色噜噜狠狠狠综合曰曰曰88av| 久久免费福利视频| 福利视频第一区| 国产九九精品视频| 亚洲色图日韩av| 精品一区电影国产| 亚洲综合在线做性| 9.1国产丝袜在线观看| 国产精品久久久久久久久久久不卡| 91成人福利在线| 亚洲一区二区在线播放| 大桥未久av一区二区三区| 日韩中文字幕亚洲| 日韩精品中文字幕在线| 国产一区二区美女视频| 久久久久久久久久久人体| 精品视频在线播放免| 中文字幕视频在线免费欧美日韩综合在线看|