继承
原型链继承
-
将父类的实例作为子类的原型对象,实现属性和方法的继承。- 优点:简单易懂,实现方便。
- 缺点:
- 多个子类实例共享同一个原型对象,操作原型对象的属性和方法会影响到所有子类实例。
- 创建子类实例时,无法向父类构造函数传参。
function Parent(){
this.name = 'father';
this.hobbit = ['sleep', 'music'];
}
function Child(){
this.name = 'child';
}
Child.prototype = new Parent(); // 所有子类实例对象都共享此原型对象
Child.prototype.constructor = Child;
var c = new Child();
var c2 = new Child();
console.log(c.hobbit); // ['sleep', 'music']
c.hobbit.push('speak');
console.log(c.hobbit) // ['sleep', 'music', 'speak']
console.log(c2.hobbit); // ['sleep', 'music', 'speak']
构造函数继承
-
在子类构造函数中调用父类构造函数,实现属性的继承。- 优点:
- 每个子类实例对象都对应独立的父类对象,不会互相影响。
- 创建子类实例时,可以向父类传递参数。
- 缺点:
- 只能继承父类的实例属性和方法,无法继承父类原型对象上的属性或方法。
- 优点:
Person.prototype.address = '辽宁沈阳';
function Person(name, age){
this.name = name;
this.age = age;
}
function Child(name, age, sex){
Person.call(this, name, age);
this.sex = sex;
}
var c = new Child('张三', 20, '男');
var c2 = new Child('李四', 30, '男');
c.type.push('TS');
console.log(c.type); // ['JS', 'HTML', 'CSS', 'TS']
console.log(c2.type) // ['JS', 'HTML', 'CSS']
console.log(c.address); // undefined 未继承Person的原型对象
组合式继承
-
将原型链继承和构造函数继承结合起来。
- 优点:
- 弥补了原型链继承和构造函数继承的缺点,保证了父类属性和方法的独立;即继承了父类的属性,又继承了父类原型对象上的属性或方法。
- 缺点:
- 会调用两次父类构造函数:一次是在创建子类型原型的时候;一次是在子类型构造函数的内部。
- 优点:
Person.prototype.address = '辽宁沈阳';
function Person(name, age){
this.name = name;
this.age = age;
this.type = ['JS','HTML','CSS'];
}
function Child(name, age, sex){
Person.call(this, name, age); // 第二次调用父类 保证了父类属性的独立性
this.sex = sex;
}
Child.prototype = new Person(); // 第一次调用父类
Child.prototype.constructor = Child;
var c = new Child('张三', 20, '男');
var c2 = new Child('李四', 20, '女');
c.type.push('TS');
console.log(c.type); // ['JS', 'HTML', 'CSS', 'TS']
console.log(c2.type) // ['JS', 'HTML', 'CSS']
console.log(c.address) // '辽宁沈阳' // 又继承了父类原型对象的属性或方法
共享原型继承
-
子类和父类共同指向一个原型对象。
- 优点:简单!
- 缺点:
- 无法继承父类构造函数的属性或方法。
- 由于指向的是一个原型对象,一有改动子类和父类的实例对象都会受影响。
Person.prototype.address = '辽宁沈阳';
Person.prototype.hobbit = ['rap', 'sing'];
function Person(){
this.name = '张三';
}
function Child(){}
Child.prototype = Person.prototype; // 子类和父类共用一个原型对象
let c = new Child();
console.log(c.name); // undefined 不能继承父类属性或方法
c.hobbit.push('sleep');
console.log(c.hobbit) // ['rap', 'sing', 'sleep']
Person.prototype.address = '辽宁大连';
console.log(c.address) // '辽宁大连' 子类和父类指向的是同一个原型对象,一改全改,无法添加独有属性
let p = new Person();
console.log(p.name); // '张三'
console.log(p.hobbit) // ['rap', 'sing', 'sleep'] 子类和父类指向的是同一个原型对象,一改全改,无法添加独有属性
原型式继承
-
通过创建一个空的构造函数,将一个对象作为该构造函数的原型对象,返回该构造函数的实例对象,实现继承。
-
优点:
- 不需要单独创建构造函数就可以实现对象继承。
-
缺点:
- 父类中的属性如果包含引用值,则会被共享。
// Object.create()等同于:
function create(obj){
function F(){} // 创建一个空的构造函数
F.prototype = obj; // 将传入的对象作为构造函数的原型对象
return new F(); // 返回构造函数的实例对象
}
let person = {
name: '张三',
hobbit:['sleep', 'music'],
sayHobbit: function(){
console.log(this.hobbit);
}
}
let zhong = Object.create(person);
zhong.name = 'zhong';
zhong.hobbit.push('moive');
zhong.sayHobbit(); // ['sleep', 'music', 'moive']
console.log(person.sayHobbit()); // ['sleep', 'music', 'moive'] 引用属性被共享
寄生式继承
- 在原型式继承的基础上添加一个包装函数,用来增强对象。返回一个新的对象,实现继承。相当于原型式继承的增强版。
- 优点:
- 不需要单独创建构造函数就可以实现对象继承。
- 缺点:
- 父类中的属性如果包含引用值,则会被共享。
let person = {
name: '张三',
hobbit:['sleep', 'music'],
sayHobbit: function(){
console.log(this.hobbit);
}
}
function createChild(obj){
let child = Object.create(obj);
child.sayName = function(){
console.log(this.name);
}
return child;
}
let child = createChild(person);
child.hobbit.push('moive');
child.sayHobbit() // ['sleep', 'music'] 继承的方法
child.sayName() // 当前对象添加的新方法
person.sayHobbit() // ['sleep', 'music', 'moive'] 引用属性被共享
寄生式组合继承
- 结合了寄生式继承与组合式继承,优化父类构造函数的调用,实现继承。
- 优点:
- 实现了完整的继承,避免了组合继承调用两次父类构造函数的缺点。
- 缺点:
- 除了代码复杂,无他!
- 优点:
// 实现继承核心代码
function inherit(subClass, parentClass){
subClass.prototype = Object.create(parentClass.prototype);
subClass.prototype.constructor = subClass;
}
Person.prototype.sayName = function(){
console.log(this.name);
}
function Person(name){
this.name = name;
this.hobbit = ['sleep', 'music'];
}
function Child(name, age){
Person.call(this, name);
this.age = age;
}
Child.prototype.sayAge = function(){
console.log(this.age);
}
inherit(Child, Person);
// c --> c.prototype --> F -->F.prototype --> parentClass.prototype
let c = new Child('张三', 20);
c.sayName(); // 张三 继承了父类原型对象的属性方法
c.hobbit // ['sleep', 'music'] 继承了父类构造函数的实例属性方法
c.hobbit.push('moive');
console.log(c.hobbit) // ['sleep', 'music', 'moive']
let c2 = new Child('李四', 18);
c2.hobbit // ['sleep', 'music'] 修改父类引用值,实例对象之间不互相影响