ES5中的继承

102 阅读6分钟

1. 原型链继承

Child.prototype = new Parent(); // 将子类的原型对象指向父类的实例

原型链继承是通过将子类的原型对象指向父类的实例来实现继承。这样子类就可以继承父类的属性和方法。但是原型链继承存在一些问题,例如所有子类实例共享同一个原型对象,无法传递参数给父类的构造函数等。

  • 通过将子类的原型对象指向父类的实例来实现继承。
  • 优点:简单易用,可以继承父类的属性和方法。
  • 缺点:所有子类实例共享父类的属性和方法,无法传递参数给父类构造函数。
  • 使用场景:适用于单层继承,不需要传递参数给父类构造函数的情况。
// 父类
function Parent() {
  this.name = 'Parent';
}

Parent.prototype.sayHello = function() {
  console.log('Hello, I am ' + this.name);
};

// 子类
function Child() {
  this.name = 'Child';
}

// 将子类的原型对象指向父类的实例
Child.prototype = new Parent();

// 创建子类的实例
var child = new Child();
child.sayHello(); // 输出: Hello, I am Child

2. 构造函数继承

Parent.call(this); // 在子类 调用父类的构造函数

构造函数继承是通过在子类的构造函数中调用父类的构造函数来实现继承。这样子类就可以继承父类的属性。但是构造函数继承无法继承父类的原型方法。

  • 在子类构造函数中调用父类构造函数,使用callapply方法绑定父类的上下文。
  • 优点:可以传递参数给父类构造函数,避免了共享属性和方法的问题。
  • 缺点:无法继承父类原型上的属性和方法。
  • 使用场景:适用于需要传递参数给父类构造函数,但不需要继承父类原型上的属性和方法的情况。
// 父类
function Parent() {
  this.name = 'Parent';
}

// 子类
function Child() {
  // 调用父类的构造函数
  Parent.call(this);
  this.age = 10;
}

// 创建子类的实例
var child = new Child();

console.log(child.name); // 输出: Parent
console.log(child.age); // 输出: 10

3. 组合继承

Parent.call(this); // 在子类 调用父类的构造函数

Child.prototype = new Parent(); // 将子类的原型对象指向父类的实例

组合继承是将原型链继承和构造函数继承结合起来的一种继承方式。通过调用父类的构造函数来继承父类的属性,并将子类的原型对象指向父类的实例来继承父类的方法。

  • 结合原型链继承和构造函数继承的方式,既继承了父类的属性和方法,又避免了共享属性和方法的问题。
  • 优点:既可以继承父类的属性和方法,又可以传递参数给父类构造函数。
  • 缺点:调用了两次父类构造函数,造成了性能上的浪费。
  • 使用场景:适用于需要继承父类的属性和方法,并且需要传递参数给父类构造函数的情况。
// 父类
function Parent() {
  this.name = 'Parent';
}

Parent.prototype.sayHello = function() {
  console.log('Hello, I am ' + this.name);
};

// 子类
function Child() {
  // 调用父类的构造函数
  Parent.call(this);
  this.age = 10;
}

// 将子类的原型对象指向父类的实例
Child.prototype = new Parent();

// 创建子类的实例
var child = new Child();

console.log(child.name); // 输出: Parent
console.log(child.age); // 输出: 10
child.sayHello(); // 输出: Hello, I am Parent

4. 原型式继承

var child = Object.create(parent); // 子类

原型式继承是通过创建一个临时的构造函数来实现继承。这个临时的构造函数的原型对象指向父类的实例,然后通过创建子类的实例来继承父类的属性和方法。

  • 使用一个临时构造函数创建一个新对象,并将其原型设置为指定的对象,实现继承。
  • 优点:简单易用,可以继承指定对象的属性和方法。
  • 缺点:所有子类实例共享父类的属性和方法,无法传递参数给父类构造函数。
  • 使用场景:适用于需要继承指定对象的属性和方法,但不需要传递参数给父类构造函数的情况。
// 父类
var parent = {
  name: 'Parent',
  sayHello: function() {
    console.log('Hello, I am ' + this.name);
  }
};

// 子类
var child = Object.create(parent);
child.age = 10;

console.log(child.name); // 输出: Parent
console.log(child.age); // 输出: 10
child.sayHello(); // 输出: Hello, I am Parent

5. 寄生式继承

function createChild() {
  var child = Object.create(parent);  // 原型式继承
  child.age = 10;  // 在子类的构造函数中增加额外的属性
  child.sayAge = function() {  // 在子类的构造函数中增加额外的方法
    console.log('I am ' + this.age + ' years old');
  };
  return child;
}

寄生式继承是在原型式继承的基础上,通过在子类的构造函数中增加额外的属性和方法来实现继承。

  • 在原型式继承的基础上,通过在新对象上添加额外的属性和方法来增强对象。
  • 优点:可以继承指定对象的属性和方法,并增加额外的属性和方法。
  • 缺点:所有子类实例共享父类的属性和方法,无法传递参数给父类构造函数。
  • 使用场景:适用于需要继承指定对象的属性和方法,并增加额外的属性和方法,但不需要传递参数给父类构造函数的情况。
// 父类
var parent = {
  name: 'Parent',
  sayHello: function() {
    console.log('Hello, I am ' + this.name);
  }
};

// 子类
function createChild() {
  var child = Object.create(parent);
  child.age = 10;
  child.sayAge = function() {
    console.log('I am ' + this.age + ' years old');
  };
  return child;
}

var child = createChild();

console.log(child.name); // 输出: Parent
console.log(child.age); // 输出: 10
child.sayHello(); // 输出: Hello, I am Parent
child.sayAge(); // 输出: I am 10 years old

6. 寄生式组合式继承

Parent.call(this, name); // 在子类调用父类的构造函数

Child.prototype = Object.create(Parent.prototype); // 创建一个父类的副本作为子类的原型对象

Child.prototype.constructor = Child; // 修复子类的构造函数指向

寄生式组合式继承结合了寄生式继承和组合继承的特点,通过借用构造函数来继承父类的属性,并通过将子类的原型对象指向一个父类的副本来继承父类的方法。这样既避免了原型链继承中父类构造函数被调用多次的问题,又能够继承父类的属性和方法。

  • 在组合继承的基础上,通过创建一个临时构造函数来继承父类的原型,并将其原型设置为父类的原型。
  • 优点:既可以继承父类的属性和方法,又可以传递参数给父类构造函数,避免了性能上的浪费。
  • 缺点:相对复杂一些。
  • 使用场景:适用于需要继承父类的属性和方法,并且需要传递参数给父类构造函数的情况。
// 父类
function Parent(name) {
  this.name = name;
}

Parent.prototype.sayHello = function() {
  console.log('Hello, I am ' + this.name);
};

// 子类
function Child(name, age) {
  // 调用父类的构造函数
  Parent.call(this, name);
  this.age = age;
}

// 创建一个父类的副本作为子类的原型对象
Child.prototype = Object.create(Parent.prototype);

// 修复子类的构造函数指向
Child.prototype.constructor = Child;

// 添加子类的方法
Child.prototype.say