JavaScript(八):继承的七种方式

一、原型链继承、构造函数继承、两者组合继承

-原型链继承

让父类的实例作为子类的原型,将子类的原型构造器补充完整 (为了让子类继承方法
做法:
(1)让子类的原型是父类的一个实例;
(2)手动给字类的原型添加构造器

function Father(name,age){
    this.name = name;
    this.gender = 'male';
    this.num = ['1']
  }
  Father.prototype.sayAge = function(){
    console.log(this.age)
  }

  function Child(age){
    this.age = age
  }

  Child.prototype = new Father(); // 子类的原型是父类的实例对象
  Child.prototype.constructor = Child;

	//子类可以重写父类的方法
  	//Child.prototype.sayAge = function(){
    //	console.log('12')
  	//}

  var c1 = new Child(20)
  console.log(c1.name) // undefined
  c1.num.push('2') 
  c1.sayAge()  // 子类的实例化对象继承了父类的方法

  var c2 = new Child(20)
  console.log(c2.num) // ['1','2']

原型继承导致的问题:
问题一 :当实例化之后,实例修改原型属性时,会影响其他的实例属性,通过上面的例子,我们发现,虽然没有给c2.num执行操作,但还是收之前实例化操作的影响,假如说你就是为了实现这样,那当然没问题,多数情况下,我们不希望各个实例之间相互影响。
问题二 :我们创建子类型实例的时候,不能给父类型的构造函数传递参数,或者说,在不影响其他实例的情况下不能传参数。
因此,我们引出了第二种继承方式:借用构造函数的方式

-构造函数继承

在子类当中去调用父类的构造函数(为了让子类继承属性

function Father(name,age){
    this.name = name;
    this.num = ['1']
    this.sayHi = function(){
    console.log('hello')
  }
  }
  
  function Child(){
    Father.call(this)
  }

  var c1 = new Child()
  console.log(c1.name) // undefined
  c1.num.push('2') 
  console.log(c1.num) // ['1','2']
  c1.sayHi()  // 可以使用父类的方法,但是不能复用

  var c2 = new Child(20)
  console.log(c2.num) // ['1']

通过这种方式,每一个实例都有自己的一份属性和方法,不受其他的实例所影响,我们想到代码的复用问题,所以这种方式还是不完美,接着引出下一种继承方式:组合继承

组合继承就是把前面的两者中方式结合起来,让属性值不受影响,让方法复用

-两者组合继承

Function Father(name){
  this.name  = name;
  this.num = ['1'];
}
Father.prototype.sayName = function(){  //父类原型的方法
  console.log(this.name);
}

function Child(age,name){
  Father.call(this,name);//继承属性
  this.age = age;
}
Child.prototype = new Father();//继承方法
Child.prototype.constructor = Child;
Child.prototype.sayAge = function(){  // 自身原型的方法
  console.log(this.age);
}

var c1 = new Child(20,"Bob");
c1.sayage(); //20
c1.sayname(); //"Bob"
c1.num.push(2);
console.log(c1.num)// [ '1' , '2' ]

var c2 = new Child(12,"Lili");
c2.sayage();//12
c2.sayname();//"Lili"
console.log(c2.num)// [ '1' ]

二、class继承

class Father{
    //在constructor里添加--实例属性
    constructor(name,age){
      this.name = name;
      this.age = age;
    }
    //实例方法——相当于:Person.Prototype.sayHi
    sayHi(){
      console.log('Hi')
    }
  }

  //使用extends 就必须继承属性,必须手动写super(),否则报错;
	//使用extends 就自动继承了父类的方法
  class Child extends Father{
    constructor(name,age,gender){
      super(name,age);  // 这是继承的属性
      this.gender = gender
    }
    //重写方法
    sayHi(){
      console.log('Hello')
    }
  }

  var c1 = new Child('Bob',12,'male')
  console.log(c1)  // Child {name: "Bob", age: 12, gender: "male"}
  c1.sayHi() //'Hello'

三、原型式继承 、寄生式继承,寄生组合继承

-原型式继承

var person = {
      name:'Bob',
      hobbies:['打篮球','唱歌']
    }

    function object(o){
      function F (){};
      F.prototype = o;
      return new F();
    }

    var obj1 = object(person)  // obj1 是 F 的实例化对象
    console.log(obj1.name)  // Bob
    obj1.hobbies.push('跳舞') 

    var obj2 = object(person)
    console.log(obj2.name) // Bob
    console.log(obj2.hobbies) //  ["打篮球", "唱歌", "跳舞"]
  • 从本质上讲, object() 对传入其中的对象执行了一次浅复制obj1.hobbies.push 之后影响了 obj2.hobbies
  • obj1 是 F 的实例化对象;obj1. __ proto __ === F.prototype === person
  • object.create() 只有一个参数时功能与上述object()方法相同 ; 它的第二个参数与Object.defineProperties()方法的第二个参数格式相同: 每个属性都是通过自己的描述符定义的.以这种方式指定的任何属性都会覆盖原型对象上的同名属性.
	var person = {
		name : "Van"
	};
	var anotherPerson = Object.create(person, {
		name : {
			value : "Louis"
		}
	});
	alert(anotherPerson.name);//"Louis"

-寄生式继承

寄生式继承是在原型式继承的基础上,用类似于工厂模式的方式,增强子类对象,即为子类添加属性和方法

var person = {
      name:'Bob',
      hobbies:['打篮球','唱歌']
    }

    function object(o){
      function F (){};
      F.prototype = o;
      return new F();
    }

  function createAnother(o){
   var newobj = object(o); //创建对象
   newobj.sayname = function(){ //增强对象
       console.log(this.name);
   }

   return newobj;//返回这个对象
}

var ob1 = createAnother(person); //ob1 是F的实例化对象
ob1.sayname();// Bob
console.log(obj1)

在这里插入图片描述
新对象newobj不仅具有 person 的所有属性和方法, 而且还被增强了, 拥有了sayname()方法.
注意: 使用寄生式继承来为对象添加函数, 会由于不能做到函数复用而降低效率;这一点与构造函数模式类似.

-寄生组合继承

前面讲过,组合继承是 JavaScript 最常用的继承模式; 不过, 它也有自己的不足. 组合继承最大的问题就是无论什么情况下,都会调用两次父类构造函数: 一次是在创建子类型原型的时候, 另一次是在子类型构造函数内部.
寄生组合式继承就是为了降低调用父类构造函数的开销而出现的 .

//寄生继承 创建一个新的对象返回一个新实例
function object(o){
  function F(){}
  F.prototype = o;
  return new F();
}
//父层,超级层
function Father(name){
  this.name = name;
  this.hobbies = ["打篮球","唱歌"];
}
Father.prototype.sayName = function(){
  console.log(this.name);
}
function Child(name,age){
  Father.call(this,name);//用构造函数方法继承属性
  this.age = age;
}

//继承父层的prototype
function inheritPrototype(child,father){
  var newObj = object(father.prototype);//返回一个新实例(对象)
  newObj.constructor = child;//增强对象
  child.prototype = newObj; //指定对象
}

//继承父层的原型
inheritPrototype(Child,Father);

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

var c1 = new Child("bob",20);
c1.hobbies.push("跳舞");
c1.sayName();//bob
c1.sayAge();//20

var c2 = new Child("lili",23);
c2.sayName();//lili
c2.sayAge();//23
console.log(c2.hobbies); //["打篮球", "唱歌"]

关系图如下:
在这里插入图片描述


参考文章
- js原型链继承,借用构造函数继承,组合继承,寄生式继承,寄生组合继承
- JS原型链与继承别再被问倒了
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值