JS中的继承
分类:javascript
继承
构造函数、原型和实例的关系
- 每个构造函数都有一个原型对象
- 原型有个属性指回构造函数
- 实例有个内部指针指向原型
原型链
- 如果原型是另一个类型的实例,那就说明这个原型本身有一个内部指针指向另一个原型,相应地另一个原型也有一个指针指向另一个构造函数。这样子就构成了一个原型链。
组合式继承
- 综合了原型链和盗用构造函数,将两者的优点集中了起来。
- 基本的思路是使用原型链继承原型上的属性和方法,而通过盗用构造函数继承实例属性。
- 这样既可以把方法在原型上以实现重用,又可以让每个实例都有自己的属性
//组合继承
function SuperType(name) {
this.name = name;
this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function() {
console.log(this.name);
}
function SubType(name, age) {
//继承属性
SuperType.call(this, name);
this.age = age;
}
SubType.prototype = new SuperType();
SuperType.prototype.sayAge = function() {
console.log(this.age);
}
let instance1 = new SubType("n1", 19);
instance1.colors.push("black");
console.log(instance1.colors); //["red", "blue", "green", "black"]
instance1.sayName(); //n1
instance1.sayAge(); //19
let instance2 = new SubType("Greg", 27);
console.log(instance2.colors); //["red", "blue", "green"]
instance2.sayName(); //Greg
instance2.sayAge(); //27
原型式继承
- 使用的情况:你有一个对象,想在它的基础上再创建一个新的对象。
- es5通过增加Object.create()方法将原型式继承的概念规范化,这个方法接收两个参数:作为新对象原型的对象,以及给新对象定义额外属性的对象(第二个可选)。在只有一个参数时,Object.create()与这里的object()方法效果相同
- 原型式继承非常适合不需要单独创建构造函数,但仍然需要在对象间共享信息的场合
let person = {
name: "Nicholas",
friends: ["Shelby", "Court", "Van"]
};
let anotherPerson = Object.create(person);
anotherPerson.name = "Greg";
anotherPerson.friends.push("Rob");
let yetAnotherPerson = Object.create(person);
yetAnotherPerson.name = "Linda";
yetAnotherPerson.friends.push("Barbie");
console.log(person.friends); //"Shelby", "Court", "Van", "Rob", "Barbie"
//第二个参数
let person1 = Object.create(person, {
name: {
value: "Grat"
}
})
console.log(person1.name);//"Grat"
寄生式继承
- 寄生式继承背后的思路类似于寄生构造函数和工厂模式:创建一个实现继承的函数,以某种方式增强对象,然后返回这个对象
- 寄生式继承同样适合主要关注对象,而不在乎类型和构造函数的场景。
let person = {
name: "Nicholas",
friends: ["Shelby", "Court", "Van"]
};
function createAnther(original) {
let clone = Object.create(original);
clone.sayHi = function() {
console.log("hi");
};
return clone;
}
let anotherPerson = createAnther(person);
anotherPerson.sayHi(); //hi
寄生式组合继承
- 组合式继承继承存在效率问题。最主要的效率问题就是父类的构造函数始终会被调用两次:一次在创建子类原型时调用,另一次是在子类构造函数中调用
- 本质上,子类原型最终是要包含超类对象的所有实例,子类构造函数只要在执行时重写自己的原型就行了。
function inheritPrototype(subType, superType) {
let prototype = Object.create(superType.prototype); //创建对象
prototype.constructor = subType;
subType.prototype = prototype;
}
function SuperType(name) {
this.name = name;
this.color = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function() {
console.log(this.name);
}
function SubType(name, age) {
SuperType.call(this, name); //第二次调用SuperType()
this.age = age;
}
inheritPrototype(SubType, SuperType);
SubType.prototype.sayAge = function() {
console.log(this.age);
}
- 原型链仍然保持不变,因此instanceof操作符和isPrototypeOf()方法正常有效。寄生式组合继承算是引用类型继承的最佳模式