JavaScript的3种继承方式

1. 原型链继承

        ECMAScript 把原型链定义为 ECMAScript 的主要继承方式。其基本思想就是通过原型继承多个引用类型的属性和方法。

        构造函数、原型和实例的关系:每个构造函数都有一个原型对象,原型有一个属性(constructor)指回构造函数,而实例有一个内部指针(__proto__)指向原型。

        基本思路:一个内部指针指向另一个原型,相应地另一个原型也有一个指针指向另一个构造函数。这样就在实例和原型之间构造了一条原型链。

// 创建Animal
function Animal() {
    this.name = 'animal';
}
Animal.prototype.getAnimalName = function () {
    console.log(this.name);
}
// 创建Dog
function Dog() {
    this.name = 'dog';
}
// Dog继承自Animal  将Animal的实例赋值给Dog的原型对象,相当于将Animal的实例中的__proto__赋值给了Dog的原型对象
Dog.prototype = new Animal();
console.log(Dog.prototype.__proto__ === Animal.prototype );  // true
// 在使用原型链继承的时候,要在继承之后再去原型对象上定义自己所需的属性和方法
Dog.prototype.getDogName = function () {
    console.log(this.name);
}
var d1 = new Dog();
d1.getAnimalName();  // dog
d1.getDogName();  // dog

优点:

  • 子类可以访问父类的所有属性、方法
  • 实现简单方便

缺点:

  • 所有子类实例共用同一个原型对象的属性
  • 创建子类实例时,无法向父类构造函数传递参数
function Animal() {
  this.arr = [1, 2];
}
function Dog() { }
// 原型链继承 Animal 
Dog.prototype = new Animal();
var d1 = new Dog();
d1.arr.push(3);
console.log(d1.arr); // [ 1,2,3 ]
var d2 = new Dog();
console.log(d2.arr); // [ 1,2,3 ]

2. 经典继承(借用构造函数继承)

基本思路 :在子类构造函数中调用父类构造函数。

function Animal(name) {
    this.name = name;
    this.arr = [1, 2];
}
function Dog(name,age) {
    // 经典继承 Animal
    Animal.call(this,name);
    this.age = age;
}
 在var d1 = new Dog()时,是d1调用Dog构造函数,所以其内部this的值指向的是d1,所以Animal.call(this)就相当于Animal.call(d1),就相当于d1.Animal()。最后,d1去调用Animal方法时,Animal内部的this指向就指向了d1。那么Animal内部this上的所有属性和方法,都被拷贝到了d1上。所以,每个实例都具有自己的arr属性副本。他们互不影响。
var d1 = new Dog('d1',12);
d1.arr.push(3);
console.log(d1.arr); // [ 1,2,3 ]
var d2 = new Dog('d2',15);
console.log(d2.arr); // [ 1,2 ]

优点:

  • 创建实例可以传递参数
  • 解决原型链继承的共用同一个父类属性问题
  • 可以实现多继承(call多个父类对象)

缺点:

  • 方法只能定义在构造函数中
  • 创建的实例并不是父类的实例,只是子类的实例。
  • 没有拼接原型链,不能使用instanceof。因为子类的实例只继承了父类的实例属性/方法,没有继承父类的构造函数的原型对象中的属性/方法。
  • 每个子类的实例都持有父类的实例方法的副本,浪费内存,影响性能,而且无法实现父类的实例方法的复用。

3. 组合继承(综合原型链继承和经典继承)

基本思路:使用原型链继承原型上的属性和方法,而通过经典继承函数继承实例属性。这样既可以把方法定义在原型上以实现重用,又可以让每个实例都有自己的属性。

function Animal(name) {
    this.name = name;
    this.arr = [1,2];
}
Animal.prototype.sayName = function () {
  console.log(this.name);
};
function Dog(name, age) {
  // 继承属性
  Animal.call(this, name);            // 第二次调用Animal()
  this.age = age;
}
// 继承方法
Dog.prototype = new Animal();            // 第一次调用Animal()
Dog.prototype.sayAge = function () {
    console.log(this.age);
};
var d1 = new Dog("d1", 12);
d1.arr.push(3);
console.log(d1.arr); // [ 1,2,3 ]
d1.sayName(); // d1
d1.sayAge(); // 12 
var d2 = new Dog("d2", 15);
console.log(d2.arr); // [ 1,2 ]
d2.sayName(); // d1
d2.sayAge(); // 15

 优点:

  • 弥补了原型链和经典继承函数的不足
  • 保留了 instanceof 操作符和 isPrototypeOf()方法识别基于组合继承创建的对象

缺点:

  • 无论什么情况下,都会调用两次超类型构造函数:一次是在创建子类型原型的时候,另一次是在子类型构造函数内部。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值