继承
继承是利用原型让一个引用类型继承另一个引用类型的属性和方法。 函数都有一个 prototype 属性,该属性指向一个对象(这个对象指的是函数的原型对象)。
原型链继承
Parent.prototype.getSuperValue = function(){
return this.property
}
function Parent(){
this.property = true
}
function Child(){
this.subproperty = false
}
Child.prototype = new Parent()
var p = new Child()
console.log(p.getSuperValue()); //true
var p2 = new Child()
console.log(p2); //Parent { subproperty: false }
缺点:
- 原型实际上会变成另一个类型的实例,并且这个实例中的属性会被所有的子类实例所共享。
function Parent () {
this.names = ['kevin', 'daisy'];
}
function Child () {
}
Child.prototype = new Parent();
var child1 = new Child();
child1.names.push('LiHua');
console.log(child1.names); // ["kevin", "daisy", "LiHua"]
var child2 = new Child();
console.log(child2.names); // ["kevin", "daisy", "LiHua"]
- 在创建子类型的实例时,做不到向父类型的构造函数传参。
构造函数继承(经典继承)
function Parent(name){
this.colors = ['red', 'green', 'blue']
}
function Child(){
Parent.call(this)
}
// Child.prototype = new Parent()
var p1 = new Child()
p1.colors.push('pink')
console.log(p1); //['red', 'green', 'blue', 'pink']
var p2 = new Child()
console.log(p2);//['red', 'green', 'blue']
优点:
- 避免了引用类型的属性被所有实例共享
- 可以在 Child 中向 Parent 传参
function Parent(name){
this.name = name
}
function Child(name){
Parent.call(this, name)
}
// Child.prototype = new Parent()
var p1 = new Child('Judy')
console.log(p1.name); //Judy
var p2 = new Child('Lisa')
console.log(p2.name);//Lisa
缺点:
- 子类实例无法继承到父类构造函数原型上的属性和方法。
- 所有的属性和方法都只能写在构造函数内部,降低了代码的复用性。
组合继承(伪经典继承)
Parent.prototype.sayname = function(){
console.log(this.name);
}
function Parent(name){
this.name = name
this.colors = ['red', 'green', 'blue']
}
function Child(name, age){
Parent.call(this,name)
this.age = age
}
Child.prototype = new Parent()
Child.prototype.constructor = Child
Child.prototype.sayAge = function(){
console.log(this.age);
}
var p1 = new Child('LiHua', 18)
p1.colors.push('yellow')
p1.sayname() //LiHua
p1.sayAge() //18
console.log(p1.colors) //[ 'red', 'green', 'blue', 'yellow' ]
var p2 = new Child('Lisa', 20)
p2.sayname() //Lisa
p2.sayAge() //20
console.log(p2.colors) //[ 'red', 'green', 'blue' ]
console.log(p1 pof Child);//true
缺点:
- 超类构造函数无论何时,都要被调用两次
原型式继承
function object(o) {
function F() {}
F.prototype = o
return new F()
}
单纯的对象之间可以胜任,包含引用类型值的属性始终会共享。
var person = {
name:'Ming',
colors:['red', 'green', 'blue']
}
var person1 = Object.create(person, {
name: {
value: 'LiHua'
}
})
var person2 = Object.create(person, {
name: {
value: 'Lisa'
}
})
person1.colors.push('yellow')
console.log(person1.name); //LiHua
console.log(person2.name); //Lisa
console.log(person1.colors); //[ 'red', 'green', 'blue', 'yellow' ]
console.log(person2.colors); //[ 'red', 'green', 'blue' ]
注意:person1.name的值,person2.name的值不互相影响,并不是因为person1和person2有独立的 name 值,而是因为person1.name = 'person1',给person1添加了 name 值,并非修改了原型上的 name 值。
寄生式继承
function createPerson(original){
var clone = Object.create(original)
clone.sayGood = function(){
console.log('hello');
}
return clone
}
let obj = {
name: 'aaa'
}
let obj2 = createPerson(obj)
console.log(obj2.name);
缺点:
- 每次创建对象都会创建一遍方法,做不到函数的复用
寄生组合式继承
Parent.prototype.sayname = function(){
console.log(this.name);
}
function Parent(name){
this.name = name
this.colors = ['red', 'green', 'blue']
}
function Child(name, age){
Parent.call(this,name)
this.age = age
}
var anotherPrototype = Object.create(Parent.prototype)
Child.prototype = anotherPrototype
Child.prototype.constructor = Child
Child.prototype.sayAge = function(){
console.log(this.age);
}
var p1 = new Child('LiHua', 18)
p1.colors.push('yellow')
p1.sayname() //LiHua
p1.sayAge() //18
console.log(p1.colors); //[ 'red', 'green', 'blue', 'yellow' ]
var p2 = new Child('Lisa', 20)
p2.sayname() //Lisa
p2.sayAge() //20
console.log(p2.colors); //[ 'red', 'green', 'blue' ]
组合继承最大的缺点是会调用两次父类构造函数。一次是设置子类型实例的原型的时候, 一次实在创建子类实例的时候。而寄生组合式继承只用调用一次父构造函数,并且因此避免了在 父类构造函数的prototype 上面创建不必要的、多余的属性。原型链能保持不变,所以能够正常使用 pof 和 isPrototypeOf。
有问题的地方欢迎大家指出,也希望大家能多和我讨论。