欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

JavaScript使用原型和原型鏈實(shí)現(xiàn)對(duì)象繼承的方法詳解

 更新時(shí)間:2017年04月05日 10:27:33   作者:番茄與西紅柿  
這篇文章主要介紹了JavaScript使用原型和原型鏈實(shí)現(xiàn)對(duì)象繼承的方法,簡單講述了javascript原型與原型鏈的原理,并結(jié)合實(shí)例形式詳細(xì)分析了javascript中對(duì)象繼承的常見實(shí)現(xiàn)技巧,需要的朋友可以參考下

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

實(shí)際上JavaScript并不是一門面向?qū)ο蟮恼Z言,不過JavaScript基于原型鏈的繼承方式、函數(shù)式語法,使得編程相當(dāng)靈活,所以可以利用原型鏈來實(shí)現(xiàn)面向?qū)ο蟮木幊獭?/p>

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

以下就是原型鏈和原型的關(guān)系,引用網(wǎng)上的一張圖

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

- 基本繼承模式

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();

此方法有優(yōu)點(diǎn)也有缺點(diǎn),繼承的實(shí)現(xiàn)很簡單,代碼簡單容易理解,但是子類繼承父類的成員變量需要自己重新初始化,相當(dāng)于父類有多少個(gè)成員變量,在子類中還需要重新定義及初始化

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屬性,如果初始化工作不斷增加,這種方式是很不方便的。因此就有了下面一種改進(jìn)的方式。

- 借用構(gòu)造函數(shù)

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()) ; //myParent
console.log(child.getName()) ; //myChild

這樣我們就只需要在子類構(gòu)造函數(shù)中執(zhí)行一次父類的構(gòu)造函數(shù),同時(shí)又可以繼承父類原型中的屬性,這也比較符合原型的初衷,就是把需要復(fù)用的內(nèi)容放在原型中,我們也只是繼承了原型中可復(fù)用的內(nèi)容。

- 臨時(shí)構(gòu)造函數(shù)模式(圣杯模式)

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

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()) ; //myParent
console.log(child.getName()) ; //myChild

個(gè)人綜合模式

Javascript模式》中到圣杯模式就結(jié)束了,可是不管上面哪一種方法都有一個(gè)不容易被發(fā)現(xiàn)的問題。大家可以看到我在'Parent'的prototype屬性中加入了一個(gè)obj對(duì)象字面量屬性,但是一直都沒有用。我們?cè)谑ケJ降幕A(chǔ)上來看看下面這種情況:

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) ; //1
console.log(parent.obj.a) ; //1
child.obj.a = 2 ;
console.log(child.obj.a) ; //2
console.log(parent.obj.a) ; //2

在上面這種情況中,當(dāng)我修改child對(duì)象obj.a的時(shí)候,同時(shí)父類的原型中的obj.a也會(huì)被修改,這就發(fā)生了和共享原型同樣的問題。出現(xiàn)這個(gè)情況是因?yàn)楫?dāng)訪問child.obj.a的時(shí)候,我們會(huì)沿著原型鏈一直找到父類的prototype中,然后找到了obj屬性,然后對(duì)obj.a進(jìn)行修改。再看看下面這種情況:

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) ; //1
console.log(parent.obj.a) ; //1
child.obj.a = 2 ;
console.log(child.obj.a) ; //2
console.log(parent.obj.a) ; //2

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

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

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) ; //1
console.log(parent.obj.a) ; //1
child.obj.a = '2' ;
console.log(child.obj.a) ; //2
console.log(parent.obj.a) ; //1

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

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 ;
  //借用父類構(gòu)造函數(shù)
  Child = function(){
    Parent.apply(this,argument) ;
  } ;
  //通過深拷貝繼承父類原型
  Child.prototype = deepClone(Parent.prototype) ;
  //重置constructor屬性
  Child.prototype.constructor = Child ;
} ;

更多關(guān)于JavaScript相關(guān)內(nèi)容感興趣的讀者可查看本站專題:《javascript面向?qū)ο笕腴T教程》、《JavaScript錯(cuò)誤與調(diào)試技巧總結(jié)》、《JavaScript數(shù)據(jù)結(jié)構(gòu)與算法技巧總結(jié)》、《JavaScript遍歷算法與技巧總結(jié)》及《JavaScript數(shù)學(xué)運(yùn)算用法總結(jié)

希望本文所述對(duì)大家JavaScript程序設(shè)計(jì)有所幫助。

相關(guān)文章

最新評(píng)論