继承
一个对象继承了另一个对象的属性和方法,就是继承。
构造函数继承的原理
js
中万物皆对象,当一个构造函数在构造对象的时候,设计者希望他们共同的属性或者方法能够共享,所以在构造函数上引入了prototype
原型属性,它包含一个对象,所有实例共享的属性和方法放在这里,不需要共享的属性放在构造函数里。实例对象创建的时候自动添加一个原型指针__proto__
指向这个原型。在实例查找不到属性时,根据原型链指针来到这个原型上查找。实例向原型上查找属性和方法这一行为就是继承。
instanceof
的原理:能在实例的原型链中找到该构造函数的原型对象,就返回true
继承的实现
假设一个有一个构造函数:
javascript
function Animal(name) {
// 实例属性
this.name = name || 'Animal';
// 实例方法
this.sleep = function () {
console.log(this.name + '正在睡觉!');
};
}
// 原型方法
Animal.prototype.eat = function (food) {
console.log(this.name + '正在吃:' + food);
};
有另一个构造函数需要继承它:
一、构造函数继承
增强子类:使用父类的构造函数来增强子类实例,等于是复制父类的实例属性给子类(没用到原型)
javascript
function Dog(name) {
Animal.call(this);
this.name = name || 'Tom';
}
// Test Code
var dog = new Dog();
console.log(dog.name);
console.log(dog.sleep());
console.log(dog.eat()); // 不存在此方法
console.log(dog instanceof Animal); // false
console.log(dog instanceof Dog); // true
问题:
- 只能继承父类的实例属性和方法,不能继承原型属性/方法
二、原型链继承
将父类的实例作为子类的原型,解决了构造继承不继承父类原型属性/方法问题
javascript
function Cat() {}
// 子类的原型指向父类的实例
Cat.prototype = new Animal();
// 修复constructor属性
Cat.prototype.constructor = Cat;
问题:
- 需要实例化父类,浪费了一次调用父类构造函数的资源
另一种原型链继承,直接继承原型:
javascript
function Cat() {}
// 子类的原型指向父类的实例
Cat.prototype = Animal.prototype;
// 修复constructor属性
Cat.prototype.constructor = Cat;
Cat.prototype.name = 'cat';
问题:
- 父类和子类共享
prototype
,修复任何一边的constructor
都会有问题! - 不能执行父类构造函数强化子类
三、组合继承
组合原型链继承和构造函数继承
javascript
function Cat() {
Animal.call(this);
this.name = name || 'Tom';
}
Cat.prototype = new Animal();
// 修复constructor属性
Cat.prototype.constructor = Cat;
问题:
- 需要实例化父类,浪费了一次调用父类构造函数的资源
四、寄生继承
将父类的原型寄生在一个空构造函数Super
上,再将子类的原型指向Super
实例对象,解决原型链继承需要实例化父类的问题。
javascript
function Cat() {}
// 创建一个没有实例方法的类
var Super = function () {};
Super.prototype = Animal.prototype;
//将实例作为子类的原型
Cat.prototype = new Super();
// 修复constructor
Cat.prototype.constructor = Dragon;
问题:
- 还是没有执行父类构造函数强化子类
五、寄生组合继承
结合组合继承和寄生继承的好处,解决原型链继承出现的问题!
javascript
function Cat() {
Animal.call(this);
this.name = name || 'Tom';
}
// 创建一个没有实例方法的类
var Super = function () {};
Super.prototype = Animal.prototype;
//将实例作为子类的原型
Cat.prototype = new Super();
// 修复constructor
Cat.prototype.constructor = Dragon;
比较完美的继承,解决了其他继承方式的问题:
- 实现了父类构造函数对子类的强化
- 不需要实例化父类
- 避免了共享
construtor
问题
六,拷贝继承
将父类实例的属性和方法拷贝到子类的原型上,使子类获得父类的属性和方法。违背了复用的初衷。
非构造函数的继承
通常是指利用深拷贝获得一个新对象,在新对象上添加属性/方法的继承方式。
Object.create(obj)
此方法创建一个新对象,将传入的obj
作为新对象__proto__
的属性值。
即:当新对象查找不到对应的属性值时,将会进入到obj
进行查找,如此实现了继承关系。