天天看點

JS繼承 原型鍊繼承、構造函數繼承、組合繼承、原型繼承、寄生式繼承、寄生組合繼承

一、原型鍊繼承

将父類的執行個體作為子類的原型
function Parent() {   this.isShow = true
   this.info = {       name: "yhd",       age: 18,
   };
}

Parent.prototype.getInfo = function() {   console.log(this.info);   console.log(this.isShow); // true}function Child() {};
Child.prototype = new Parent();let Child1 = new Child();
Child1.info.gender = "男";
Child1.getInfo();  // {name: "yhd", age: 18, gender: "男"}let child2 = new Child();
child2.getInfo();  // {name: "yhd", age: 18, gender: "男"}child2.isShow = falseconsole.log(child2.isShow); // false複制代碼      

優點:

1、父類方法可以複用

缺點:

  1. 父類的所有引用屬性(info)會被所有子類共享,更改一個子類的引用屬性,其他子類也會受影響
  2. 子類型執行個體不能給父類型構造函數傳參

二、盜用構造函數繼承(構造函數繼承)

在子類構造函數中調用父類構造函數,可以在子類構造函數中使用call()和apply()方法
function Parent() {  this.info = {name: "yhd",age: 19,
  }
}function Child() {
    Parent.call(this)
}let child1 = new Child();
child1.info.gender = "男";console.log(child1.info); // {name: "yhd", age: 19, gender: "男"};let child2 = new Child();console.log(child2.info); // {name: "yhd", age: 19}複制代碼      

通過使用call()或apply()方法,Parent構造函數在為Child的執行個體建立的新對象的上下文執行了,就相當于新的Child執行個體對象上運作了Parent()函數中的所有初始化代碼,結果就是每個執行個體都有自己的info屬性。

1、傳遞參數

相比于原型鍊繼承,盜用構造函數的一個優點在于可以在子類構造函數中像父類構造函數傳遞參數。

function Parent(name) {this.info = { name: name };
}function Child(name) {//繼承自Parent,并傳參Parent.call(this, name);    
     //執行個體屬性this.age = 18}let child1 = new Child("yhd");console.log(child1.info.name); // "yhd"console.log(child1.age); // 18let child2 = new Child("wxb");console.log(child2.info.name); // "wxb"console.log(child2.age); // 18複制代碼      

在上面例子中,Parent構造函數接收一個name參數,并将他指派給一個屬性,在Child構造函數中調用Parent構造函數時傳入這個參數, 實際上會在Child執行個體上定義name屬性。為確定Parent構造函數不會覆寫Child定義的屬性,可以在調用父類構造函數之後再給子類執行個體添加額外的屬性

優點:

  1. 可以在子類構造函數中向父類傳參數
  2. 父類的引用屬性不會被共享
  1. 子類不能通路父類原型上定義的方法(即不能通路Parent.prototype上定義的方法),是以所有方法屬性都寫在構造函數中,每次建立執行個體都會初始化

三、組合繼承

組合繼承綜合了原型鍊繼承和盜用構造函數繼承(構造函數繼承),将兩者的優點結合了起來,

基本的思路就是使用原型鍊繼承原型上的屬性和方法,而通過構造函數繼承執行個體屬性,這樣既可以把方法定義在原型上以實作重用,又可以讓每個執行個體都有自己的屬性

function Parent(name) {   this.name = name   this.colors = ["red", "blue", "yellow"]
}
Parent.prototype.sayName = function () {   console.log(this.name);
}function Child(name, age) {   // 繼承父類屬性
   Parent.call(this, name)   this.age = age;
}// 繼承父類方法Child.prototype = new Parent();

Child.prototype.sayAge = function () {   console.log(this.age);
}let child1 = new Child("yhd", 19);
child1.colors.push("pink");console.log(child1.colors); // ["red", "blue", "yellow", "pink"]child1.sayAge(); // 19child1.sayName(); // "yhd"let child2 = new Child("wxb", 30);console.log(child2.colors);  // ["red", "blue", "yellow"]child2.sayAge(); // 30child2.sayName(); // "wxb"複制代碼      

上面例子中,Parent構造函數定義了name,colors兩個屬性,接着又在他的原型上添加了個sayName()方法。Child構造函數内部調用了Parent構造函數,同時傳入了name參數,同時Child.prototype也被指派為Parent執行個體,然後又在他的原型上添加了個sayAge()方法。這樣就可以建立 child1,child2兩個執行個體,讓這兩個執行個體都有自己的屬性,包括colors,同時還共享了父類的sayName方法

  1. 父類的方法可以複用
  2. 可以在Child構造函數中向Parent構造函數中傳參
  3. 父類構造函數中的引用屬性不會被共享

四、原型式繼承

對參數對象的一種淺複制
function objectCopy(obj) {  function Fun() { };
  Fun.prototype = obj;  return new Fun()
}let person = {  name: "yhd",  age: 18,  friends: ["jack", "tom", "rose"],  sayName:function() {console.log(this.name);
  }
}let person1 = objectCopy(person);
person1.name = "wxb";
person1.friends.push("lily");
person1.sayName(); // wxblet person2 = objectCopy(person);
person2.name = "gsr";
person2.friends.push("kobe");
person2.sayName(); // "gsr"console.log(person.friends); // ["jack", "tom", "rose", "lily", "kobe"]複制代碼      
  1. 父類方法可複用
  1. 父類的引用會被所有子類所共享
  2. 子類執行個體不能向父類傳參
ES5的Object.create()方法在隻有第一個參數時,與這裡的objectCopy()方法效果相同

五、寄生式繼承

function objectCopy(obj) {  function Fun() { };
  Fun.prototype = obj;  return new Fun();
}function createAnother(original) {  let clone = objectCopy(original);
  clone.getName = function () {console.log(this.name);
  };  return clone;
}let person = {     name: "yhd",     friends: ["rose", "tom", "jack"]
}let person1 = createAnother(person);
person1.friends.push("lily");console.log(person1.friends);
person1.getName(); // yhdlet person2 = createAnother(person);console.log(person2.friends); // ["rose", "tom", "jack", "lily"]複制代碼      

六、寄生式組合繼承

function objectCopy(obj) {
  function Fun() { };
  Fun.prototype = obj;
  return new Fun();
}

function inheritPrototype(child, parent) {
  let prototype = objectCopy(parent.prototype); // 建立對象
  prototype.constructor = child; // 增強對象
  Child.prototype = prototype; // 指派對象
}

function Parent(name) {
  this.name = name;
  this.friends = ["rose", "lily", "tom"]
}

Parent.prototype.sayName = function () {
  console.log(this.name);
}

function Child(name, age) {
  Parent.call(this, name);
  this.age = age;
}

inheritPrototype(Child, Parent);
Child.prototype.sayAge = function () {
  console.log(this.age);
}

let child1 = new Child("yhd", 23);
child1.sayAge(); // 23
child1.sayName(); // yhd
child1.friends.push("jack");
console.log(child1.friends); // ["rose", "lily", "tom", "jack"]

let child2 = new Child("yl", 22)
child2.sayAge(); // 22
child2.sayName(); // yl
console.log(child2.friends); // ["rose", "lily", "tom"]複制代碼      
  1. 隻調用一次父類構造函數
  2. Child可以向Parent傳參
  3. 父類方法可以複用

繼續閱讀