原型链继承
function Father() {
this.name = 'jiangyao';
this.food = ['rice', 'sandwich']
}
function Son() {}
Son.prototype = new Father() //子类原型作为父类的实例
var son1 = new Son(); //子类实例son1
var son2 = new Son(); //子类实例son2
son1.name = 'zs';
son1.food.push('apple');
console.log(son2.name) //jiangyao
console.log(son2.food) //[ 'rice', 'sandwich', 'apple' ]
优点:
子类可以继承父类构造函数的方法及原型上的方法。
缺点:
1.某一子类更改父类的引用属性(上文food),其他子类也会受到影响;
2.子类在实例化时不能给父类的构造函数传参。
盗用构造函数
在子类构造函数中使用call()和apply()方法继承父类
function Father(name) {
this.name = name;
this.food = ['rice', 'sandwich']
}
Father.prototype.say = function() {
console.log('hello')
}
function Son() {
Father.call(this,'jiangyao')
// Father.apply(this, ['jiangyao'])
//call()与apply的区别是apply传参需要是数组形式
}
var son1 = new Son()
var son2 = new Son()
console.log(son1.name) //jiangyao
son1.food.push('single')
console.log(son1.food) //[ 'rice', 'sandwich', 'single' ]
console.log(son2.food) //[ 'rice', 'sandwich' ]
son1.say(); //son1.say is not a function
优点:
1.某一子类更改父类的引用属性,不会影响其他子类;
2.在子类构造函数种可以向父类传参。
缺点:
子类不能继承父类原型上的属性。
组合继承
组合继承综合了原型链继承和盗用构造函数继承,将两者的优点结合了。
function Father(name) {
this.name = name;
this.food = ['rice', 'sandwich']
}
Father.prototype.say = function() {
console.log('hello')
}
function Son() {
Father.call(this, 'jiangyao') //盗用构造函数继承 第二次调用Father()
}
Son.prototype = new Father() //原型链继承 第一次调用Father()
let son1 = new Son()
son1.say(); //hello
原型式继承
本质上,是对传入的对象执行了一次浅复制,此继承方式与原型链继承效果一样,属性中包含的引用值始终会在相关对象间共享,见下面例子
function object(obj) {
function F() {}
F.prototype = obj;
return new F()
}
let person = {
name: 'jiangyao',
friends: ['Jy', 'Mary']
}
let anotherPerson = object(person)
anotherPerson.name = 'zs';
anotherPerson.friends.push('Jack')
console.log(anotherPerson.name) //zs
let yetAnotherPerson = object(person)
yetAnotherPerson.name = 'lisi';
yetAnotherPerson.friends.push('Susan')
console.log(yetAnotherPerson.name) //lisi
console.log(person.friends) //[ 'Jy', 'Mary', 'Jack', 'Susan' ]
原型式继承适用于你有一个对象,你想在它的基础上再创建一个新对象。此方法不需要单独创建构造函数便可以实现对象间共享信息。ES5的Object.create()方法在只有第一个参数时,与这里的object()方法效果相同。
寄生式继承
与原型式继承比较接近的一种继承方式,以某种方式增强对象。
function object(obj) {
function F() {}
F.prototype = obj;
return new F()
}
function createAnother(original) {
let clone = object(original); //通过调用函数创建一个新对象
clone.sayHi = function() { //以某种方式增强这个对象
console.log('Hi');
};
return clone;
}
let person = {
name: 'jiangyao',
friends: ['Jy', 'Mary']
}
let anotherPerson = createAnother(person); //基于person对象返回的新对象
anotherPerson.sayHi(); //Hi 具有person所有的属性和方法,还有自己的新方法sayHi()
上文object()函数不是寄生式继承必需的,任何返回的新对象都可以在这里使用。
寄生式组合继承
组合继承其实也存在效率问题,最主要的效率问题就是父类构造函数始终会被调用两次:一次是在创建子类原型是调用,另一次是在子类构造函数中调用。本质上,子类原型最终是要包含超类对象所有实例属性,子类构造函数只要在执行时重写自己的原型就行了。
function object(obj) {
function F() {}
F.prototype = obj;
return new F()
}
function inheritPrototype(son, father) {
let prototype = object(father.prototype); // 创建对象
prototype.constructor = son; // 增强对象
son.prototype = prototype; // 赋值对象
}
function Father(name) {
this.name = name;
this.colors = ['red', 'blue', 'pink']
}
Father.prototype.sayName = function() {
console.log(this.name);
}
function Son(name, age) {
Father.call(this, name);
this.age = age;
}
inheritPrototype(Son, Father);
Son.prototype.sayAge = function() {
console.log(this.age);
}
let son1 = new Son('jy', 21)
son1.colors.push('green')
console.log(son1.name) //jy
console.log(son1.age) //21
console.log(son1.colors) //[ 'red', 'blue', 'pink', 'green' ]
let son2 = new Son('jack', 22)
son2.sayAge(); //jack
son2.sayName(); //22
console.log(son2.colors) //[ 'red', 'blue', 'pink' ]
在这里只调用了一次Father()构造函数,避免了Father.prototype上不必要也用不到的属性,因此可以说这个继承方法的效率更高。寄生式组合继承算是引用类型继承的最佳模式。