国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > JavaScript > 正文

JavaScript使用原型和原型鏈實現對象繼承的方法詳解

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

本文實例講述了JavaScript使用原型和原型鏈實現對象繼承的方法。分享給大家供大家參考,具體如下:

實際上JavaScript并不是一門面向對象的語言,不過JavaScript基于原型鏈的繼承方式、函數式語法,使得編程相當靈活,所以可以利用原型鏈來實現面向對象的編程。

之前對JavaScript一直都是一知半解,這兩天看了一下原型鏈這一塊知識,綜合練習了一下JavaScript的對象繼承方式。

以下就是原型鏈和原型的關系,引用網上的一張圖

在Javascript中,每個函數都有一個原型屬性prototype指向自身的原型,而由這個函數創建的對象也有一個proto屬性指向這個原型,而函數的原型是一個對象,所以這個對象也會有一個proto指向自己的原型,這樣逐層深入直到Object對象的原型,這樣就形成了原型鏈。

- 基本繼承模式

function FatherClass() {  this.type = 'father';}FatherClass.prototype.getTyep = function() {  console.log(this.type);}FatherClass.prototype.obj = {age: 35};function ChildClass() {  this.type = 'child';}ChildClass.prototype = FatherClass();ChildClass.prototype.getType = function() {  console.log(this.type);}var father = new FatherClass();var child = new ChildClass();father.getTyep();child.getType();

此方法有優點也有缺點,繼承的實現很簡單,代碼簡單容易理解,但是子類繼承父類的成員變量需要自己重新初始化,相當于父類有多少個成員變量,在子類中還需要重新定義及初始化

function FatherClass(type) {  this.type = type || 'father';}function ChildClass(type) {  this.type = type || 'child';}ChildClass.prototype = FatherClass();ChildClass.prototype.getType = function() {  console.log(this.type);}var father = new FatherClass('fatClass');var child = new ChildClass('chilClass');

上面這種情況還只是需要初始化name屬性,如果初始化工作不斷增加,這種方式是很不方便的。因此就有了下面一種改進的方式。

- 借用構造函數

var Parent = function(name){  this.name = name || 'parent' ;} ;Parent.prototype.getName = function(){  return this.name ;} ;Parent.prototype.obj = {a : 1} ;var Child = function(name){  Parent.apply(this,arguments) ;} ;Child.prototype = Parent.prototype ;var parent = new Parent('myParent') ;var child = new Child('myChild') ;console.log(parent.getName()) ; //myParentconsole.log(child.getName()) ; //myChild

這樣我們就只需要在子類構造函數中執行一次父類的構造函數,同時又可以繼承父類原型中的屬性,這也比較符合原型的初衷,就是把需要復用的內容放在原型中,我們也只是繼承了原型中可復用的內容。

- 臨時構造函數模式(圣杯模式)

上面借用構造函數模式最后改進的版本還是存在問題,它把父類的原型直接賦值給子類的原型,這就會造成一個問題,就是如果對子類的原型做了修改,那么這個修改同時也會影響到父類的原型,進而影響父類對象,這個肯定不是大家所希望看到的。為了解決這個問題就有了臨時構造函數模式。

var Parent = function(name){  this.name = name || 'parent' ;} ;Parent.prototype.getName = function(){  return this.name ;} ;Parent.prototype.obj = {a : 1} ;var Child = function(name){  Parent.apply(this,arguments) ;} ;var F = new Function(){} ;F.prototype = Parent.prototype ;Child.prototype = new F() ;var parent = new Parent('myParent') ;var child = new Child('myChild') ;console.log(parent.getName()) ; //myParentconsole.log(child.getName()) ; //myChild

個人綜合模式

Javascript模式》中到圣杯模式就結束了,可是不管上面哪一種方法都有一個不容易被發現的問題。大家可以看到我在'Parent'的prototype屬性中加入了一個obj對象字面量屬性,但是一直都沒有用。我們在圣杯模式的基礎上來看看下面這種情況:

var Parent = function(name){  this.name = name || 'parent' ;} ;Parent.prototype.getName = function(){  return this.name ;} ;Parent.prototype.obj = {a : 1} ;var Child = function(name){  Parent.apply(this,arguments) ;} ;var F = new Function(){} ;F.prototype = Parent.prototype ;Child.prototype = new F() ;var parent = new Parent('myParent') ;var child = new Child('myChild') ;console.log(child.obj.a) ; //1console.log(parent.obj.a) ; //1child.obj.a = 2 ;console.log(child.obj.a) ; //2console.log(parent.obj.a) ; //2

在上面這種情況中,當我修改child對象obj.a的時候,同時父類的原型中的obj.a也會被修改,這就發生了和共享原型同樣的問題。出現這個情況是因為當訪問child.obj.a的時候,我們會沿著原型鏈一直找到父類的prototype中,然后找到了obj屬性,然后對obj.a進行修改。再看看下面這種情況:

var Parent = function(name){  this.name = name || 'parent' ;} ;Parent.prototype.getName = function(){  return this.name ;} ;Parent.prototype.obj = {a : 1} ;var Child = function(name){  Parent.apply(this,arguments) ;} ;var F = new Function(){} ;F.prototype = Parent.prototype ;Child.prototype = new F() ;var parent = new Parent('myParent') ;var child = new Child('myChild') ;console.log(child.obj.a) ; //1console.log(parent.obj.a) ; //1child.obj.a = 2 ;console.log(child.obj.a) ; //2console.log(parent.obj.a) ; //2

這里有一個關鍵的問題,當對象訪問原型中的屬性時,原型中的屬性對于對象來說是只讀的,也就是說child對象可以讀取obj對象,但是無法修改原型中obj對象引用,所以當child修改obj的時候并不會對原型中的obj產生影響,它只是在自身對象添加了一個obj屬性,覆蓋了父類原型中的obj屬性。而當child對象修改obj.a時,它先讀取了原型中obj的引用,這時候child.obj和Parent.prototype.obj是指向同一個對象的,所以child對obj.a的修改會影響到Parent.prototype.obj.a的值,進而影響父類的對象。AngularJS中關于$scope嵌套的繼承方式就是模范Javasript中的原型繼承來實現的。

根據上面的描述,只要子類對象中訪問到的原型跟父類原型是同一個對象,那么就會出現上面這種情況,所以我們可以對父類原型進行拷貝然后再賦值給子類原型,這樣當子類修改原型中的屬性時就只是修改父類原型的一個拷貝,并不會影響到父類原型。具體實現如下:

var deepClone = function(source,target){  source = source || {} ;  target = target || {};  var toStr = Object.prototype.toString ,    arrStr = '[object array]' ;  for(var i in source){    if(source.hasOwnProperty(i)){      var item = source[i] ;      if(typeof item === 'object'){        target[i] = (toStr.apply(item).toLowerCase() === arrStr) ? [] : {} ;        deepClone(item,target[i]) ;      }else{        target[i] = item;      }    }  }  return target ;} ;var Parent = function(name){  this.name = name || 'parent' ;} ;Parent.prototype.getName = function(){  return this.name ;} ;Parent.prototype.obj = {a : '1'} ;var Child = function(name){  Parent.apply(this,arguments) ;} ;Child.prototype = deepClone(Parent.prototype) ;var child = new Child('child') ;var parent = new Parent('parent') ;console.log(child.obj.a) ; //1console.log(parent.obj.a) ; //1child.obj.a = '2' ;console.log(child.obj.a) ; //2console.log(parent.obj.a) ; //1

綜合上面所有的考慮,Javascript繼承的具體實現如下,這里只考慮了Child和Parent都是函數的情況下:

var deepClone = function(source,target){  source = source || {} ;  target = target || {};  var toStr = Object.prototype.toString ,    arrStr = '[object array]' ;  for(var i in source){    if(source.hasOwnProperty(i)){      var item = source[i] ;      if(typeof item === 'object'){        target[i] = (toStr.apply(item).toLowerCase() === arrStr) ? [] : {} ;        deepClone(item,target[i]) ;      }else{        target[i] = item;      }    }  }  return target ;} ;var extend = function(Parent,Child){  Child = Child || function(){} ;  if(Parent === undefined)    return Child ;  //借用父類構造函數  Child = function(){    Parent.apply(this,argument) ;  } ;  //通過深拷貝繼承父類原型  Child.prototype = deepClone(Parent.prototype) ;  //重置constructor屬性  Child.prototype.constructor = Child ;} ;

更多關于JavaScript相關內容感興趣的讀者可查看本站專題:《javascript面向對象入門教程》、《JavaScript錯誤與調試技巧總結》、《JavaScript數據結構與算法技巧總結》、《JavaScript遍歷算法與技巧總結》及《JavaScript數學運算用法總結

希望本文所述對大家JavaScript程序設計有所幫助。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 邯郸市| 蒙山县| 玉门市| 肇东市| 金山区| 宝应县| 西盟| 寻乌县| 龙陵县| 稷山县| 浮山县| 翼城县| 娱乐| 云龙县| 且末县| 阿坝县| 上犹县| 丁青县| 松江区| 保山市| 大埔县| 临城县| 广饶县| 邛崃市| 阿合奇县| 南阳市| 德庆县| 五指山市| 察雅县| 南雄市| 长泰县| 运城市| 道真| 麻阳| 岚皋县| 丰顺县| 神农架林区| 青海省| 广宗县| 密山市| 嘉善县|