首頁 >web前端 >js教程 >(超經典)javascript中物件繼承方式的總結

(超經典)javascript中物件繼承方式的總結

不言
不言原創
2018-09-03 10:44:521374瀏覽

這篇文章帶給大家的內容是關於(超經典)javascript中物件繼承方式的總結 ,有一定的參考價值,有需要的朋友可以參考一下,希望對你有所幫助。

一、原型鏈繼承

重點:利用原型讓一個參考型別繼承另外一個引用型別的屬性和方法。建構函數,原型,實例之間的關係:每個建構函數都有一個原型對象,原型對象包含一個指向建構函數的指針,而實例都包含一個指向原型對象的內部指針。

function SuperType(){
  this.property = true;
}
SuperType.prototype.getSuperValue = function(){
  return this.property;
};

function SubType(){
  this.subproperty = false;
}
// 继承自SuperType
SubType.prototype = new SuperType();

SubType.prototype.getSubValue = function (){
  return this.subproperty;
};

var example = new SubType();
alert(example.getSuperValue());//true

使用原型建立物件會存在多個實例對引用類型的操作會被竄改的問題,在上面同樣存在這個問題,如下:

function SuperType(){
  this.colors = ["red", "blue", "green"];
}
function SubType(){}//即使没有写,也不会影响结果

SubType.prototype = new SuperType();

var example1 = new SubType();
example1.colors.push("black");
alert(example1.colors); //"red,blue,green,black"

var example2 = new SubType(); 
alert(example.colors); //"red,blue,green,black"

兩個實例物件example1和example2的colors屬性指向相同,改變一個會影響另一個實例的屬性。

缺點:
①原型鏈繼承多個實例的引用類型屬性指向相同,一個實例修改了原型屬性,另一個實例的原型屬性也會被修改;
②不能傳遞參數;
③繼承單一。

二、借用建構子繼承

#重點:使用.call()和.apply()將父類別建構子引入子類別函數,使用父類別的建構子來增強子類別實例,等同於複製父類別的實例給子類別。

function SuperType(name){
  this.name = name;
  this.colors = ["red", "blue", "green"];
}

function SubType(name, age){
  // 继承自SuperType
  SuperType.call(this, name);  
  this.age = age;
}

var example1 = new SubType("Mike", 23);
example1.colors.push("black");
alert(example1.colors);//"red,blue,green,black"

var example2 = new SubType();
alert(example2.colors);//"red,blue,green"

alert(example1.name); // "Mike"
alert(example1.age); // 23

借用建構函式繼承的重點就在於SuperType.call(this, name),呼叫了SuperType建構函數,這樣,SubType的每個實例都會將SuperType中的屬性複製一份。

缺點:
①只能繼承父類別的實例屬性和方法,不能繼承原型屬性/方法;
②無法實作建構函式的複用,每個子類別都有父類別實例函數的副本,影響效能,程式碼會臃腫。

三、組合繼承

重點:將原型鏈繼承建構子繼承這兩種模式的優點組合在一起,透過呼叫父類別構造,繼承父類別的屬性並保留傳參,然後透過將父類別實例作為子類別原型,實現函數復用。

背後的想法是使用原型鏈實現對原型屬性和方法的繼承,而透過借用建構函式來實現對實例屬性的繼承,這樣,既透過在原型上定義方法實現了函數復用,又能保證每個實例都有它自己的屬性。

function SuperType(name){
  this.name = name;
  this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function(){
  alert(this.name);
};

function SubType(name, age){
  //继承属性
  SuperType.call(this, name);
  this.age = age;
}

// 继承方法
SubType.prototype = new SuperType(); 
SubType.prototype.constructor = SubType; 
SubType.prototype.sayAge = function(){
    alert(this.age);
};

var example1 = new SubType("Mike", 23);
example1.colors.push("black");
alert(example1.colors); //"red,blue,green,black"
example1.sayName(); //"Mike";
example1.sayAge(); //23

var example2 = new SubType("Jack", 22);
alert(example2.colors); //"red,blue,green"
example2.sayName(); //"Jack";
example2.sayAge(); //22

缺陷:
父類別中的實例屬性和方法既存在於子類別的實例中,又存在於子類別的原型中,不過僅是記憶體佔用,因此,在使用子類別中創建實例物件時,其原型中會存在兩份相同的屬性/方法。 -------這個方法是javascript中最常用的繼承模式

四、原型式繼承

#重點:用一個函數包裝一個對象,然後返回這個函數的調用,這個函數就變成了個可以隨意增添屬性的實例或物件。 object.create()就是這個原理,直接將某個物件直接賦值給建構函數的原型。

function object(obj){
  function O(){}
  O.prototype = obj;
  return new O();
}

object()對傳入其中的物件執行了一次淺複製,將O的原型直接指向傳入的物件。

var person = {
  name: "Mike",
  friends: ["Jack", "Tom", "Joes"]
};

var anotherPerson = object(person);
anotherPerson.name = "Greg";
anotherPerson.friends.push("Peter");

var yetAnotherPerson = object(person);
yetAnotherPerson.name = "Linda";
yetAnotherPerson.friends.push("BoBo");

alert(person.friends);   //"Jack,Tom,Joes,Peter,BoBo"

ECMAScript5透過新增Object.create()方法規範化了原型式繼承,這個方法接收兩個參數:一個用作新物件原型的物件和一個作為新物件定義額外屬性的物件。

var person = {
name:"EvanChen",
friends:["Shelby","Court","Van"];
};
var anotherPerson = Object.create(person);
anotherPerson.name = "Greg";
anotherPerson.friends.push("Rob");
var yetAnotherPerson = Object.create(person);
yetAnotherPerson.name = "Linda";
yetAnotherPerson.friends.push("Barbie");
console.log(person.friends);//"Shelby","Court","Van","Rob","Barbie"

缺點:
①原型鏈繼承多個實例的引用類型屬性指向相同(所有實例都會繼承原型上的屬性),存在篡改的可能;
②無法傳遞參數,無法實現復用。 (新實例屬性都是後面新增的)。

五、寄生式繼承

#重點:建立一個僅用於封裝繼承過程的函數,該函數在內部以某種方式來增強對象,最後返回構造函數。 (就像給原型式繼承外面套了個殼子,然後return出來)

function createAnother(original){ 
  varclone=object(original); // 过调用函数创建一个新对象
  clone.sayHi = function(){ // 以某种方式增强这个对象
    alert("hi");
  };
  return clone; // 返回对象
}

函數的主要作用是為構造函數新增屬性和方法,以增強函數。

var person = {
  name: "Nicholas",
  friends: ["Shelby", "Court", "Van"]
};
var anotherPerson = createAnother(person);
anotherPerson.sayHi(); //"hi"

缺點:
①原型鏈繼承多個實例的引用類型屬性指向相同,存在篡改的可能;
②無法傳遞參數,沒用到原型,無法重複使用。

六、寄生組合式繼承

#重點:透過借用建構函式傳遞參數和寄生模式實現繼承屬性,透過原型鏈的混成形式來繼承方法,在函數中用apply或call引入另一個建構函數,可傳參。

function inheritPrototype(subType, superType){
  var prototype = Object.create(superType.prototype); //Object.create创建对象
  prototype.constructor = subType;                    // 增强对象
  subType.prototype = prototype;                      // 指定对象
}

// 父类初始化实例属性和原型属性
function SuperType(name){
  this.name = name;
  this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function(){
  alert(this.name);
};

// 借用构造函数传递增强子类实例属性(支持传参和避免篡改)
function SubType(name, age){
  SuperType.call(this, name);
  this.age = age;
}

// 将父类原型指向子类
inheritPrototype(SubType, SuperType);

// 新增子类原型属性
SubType.prototype.sayAge = function(){
  alert(this.age);
}

var example1 = new SubType("abc", 21);
var example2 = new SubType("def", 22);

example1.colors.push("pink"); // ["red", "blue", "green", "pink"]
example1.colors.push("black"); // ["red", "blue", "green", "black"]

寄生組合繼承集合了前面幾種繼承優點,幾乎避免了上面繼承方式的所有缺陷,是執行效率最高也是應用面最廣的。

缺點:
實作的過程相對繁瑣。

為什麼要學習這些繼承方式,明明可以直接繼承為什麼還要搞這麼麻煩? 主要是為了學習它們的思想,打下更好的基礎,為以後閱讀框架源碼,或自己封裝組件甚至框架大有益處。

時間有點匆忙,沒有加上ES6的extends。

相關推薦:

JavaScript物件導向繼承方法

實作JavaScript中繼承的三種方式_js物件導向

以上是(超經典)javascript中物件繼承方式的總結的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn