1. 原型链继承
function Person(){
this.name = 'Person'
}
Person.prototype.getName = function(){
console.log(this.name)
}
function Child(){}
Child.prototype = new Person() // 子类的prototype指向父类的实例
let child1 = new Child()
child1.getName()
- 重点:子类的
prototype
指向父类的实例,子类的实例的原型指向父类的实例。 - 优点:实例可继承的属性有:(新实例不会继承父类实例的属性)
- 实例的构造函数的属性
- 父类构造函数的属性
- 父类原型对象的属性
- 缺点:
- 新实例实例化时无法向父类构造函数传参
- 继承单一(无法实现多继承)
- 所有新实例会共享父类实例的属性
2. 构造函数
继承(盗用构造函数 / 对象伪装 / 经典继承)
function Person(){
this.name = 'xiaoming';
this.colors = ['red', 'blue', 'green'];
}
Person.prototype.getName = function(){
console.log(this.name);
}
function Child(age){
// 子类中调用父类的构造函数,可以传参,使用call修改this
Person.call(this);
this.age = age
}
var child1 = new Child(23);
var child2 = new Child(12);
child1.colors.push('yellow');
console.log(child1.name); // xiaoming
console.log(child1.colors); // ["red", "blue", "green", "yellow"]
console.log(child2.colors); // ["red", "blue", "green"]
- 重点:用call或apply将父类构造函数引入子类函数中(在子函数中做了父类函数的自执行)
- 优点:
- 继承了父类构造函数的属性
- 解决了原型链继承缺点123
- 可以继承多个构造函数(
多继承
) - 在子实例可以向父实例
传参
- 缺点:
- 只能继承父类构造函数的属性,
没有继承父类原型的属性
(父类的prototype没有在子类实例的原型链上) - 无法实现构造函数的复用(每次用每次都要重新调用)
- 每个新实例都有父类构造函数的副本(复制,臃肿)
- 只能继承父类构造函数的属性,
3. 组合继承(原型链 + 构造函数)
function Parent(name){
this.name = name;
this.colors = ['red', 'blue', 'green'];
}
Parent.prototype.getName = function(){
console.log(this.name);
}
function Child(name,age){
Parent.call(this,name);// 第二次调用 Parent(),构造函数继承
this.age = age;
}
Child.prototype = new Parent(); // 第一次调用 Parent(),原型链继承
var child1 = new Child('xiaopao',18);
var child2 = new Child('lulu',19);
- 重点:两种结合:引入构造函数 + 原型指向父类实例
- 优点:
- 可以继承父类原型上的属性,可以传参 / 复用
- 每个新实例引入的构造函数属性是私有的
- 缺点:
- 调用了两次父类的构造函数(耗内存),子类的构造函数会代替原型上的那个父类构造函数(重复了,子类实例屏蔽了父类实例的属性)
4. 原型式继承
其实就是用Object.create
,过程:
- 创建一个函数
- 函数的prototype指向传入的对象
- 使用
new
创建实例,并return
(示例的的 __proto__指向传入的对象)
function CreateObj(o){
function F(){} // 1
F.prototype = o; // 2
console.log(o.__proto__ === Object.prototype);
console.log(F.prototype.constructor === Object); // true
return new F(); // 3
}
var person = {
name: 'xiaopao',
friend: ['daisy','kelly']
}
var person1 = CreateObj(person);
person1.name = 'person1';
person1.friend.push('taylor');
// console.log(person2.friend); // ["daisy", "kelly", "taylor"]"taylor"]
- 重点:Object.create同原理。(一个CreateObj的函数,接收一个对象,内部声明一个构造函数,其原型对象指向所传入对象,最后返回使用new得到的内部构造函数的实例)
- 优点:
- 类似于复制一个对象,用函数来包装。
- 不需要单独的构造函数
- 缺点:
- 所有实例都会继承原型上的属性
- 无法实现复用(新实例属性都是后面添加的)
5. 寄生式继承
在原型式继承外再封装了一层用以修改实例的属性。
function createAnother(original){
let clone = object(original)
clone.sayhi = () => { //以某种方式增强示例
console.log('hi')
}
return clone
}
6. 寄生组合式继承
- 使用借用构造函数来继承父类中this声明的属性和方法
- 使用寄生式继承来设置父类prototype为子类prototype的原型来继承父类的属性和方法
function SuperType(name){
this.name = name;
this.colors = [];
}
function SubType(name, age){
SuperType.call(this,name); // 盗用继承函数
this.age = age;
}
let prototype = Object.create(superType.prototype); // 原型式
SubType.prototype = prototype; // 寄生式
prototype.constructor = SubType;
- 只调用一次SuperType
- instanceof / isPrototypeOf 正常
7. 类的继承
ES6 的继承机制的实质是先将父类实例对象的属性和方法,加到this上面(所以先调用super方法),然后再用子类的构造函数修改this。
子类继承父类:class 子类 extends 父类;在子类的构造方法中调用父类的构造方法:super()。
// class继承
class Parent {
constructor(name, gender) {
this.name = name;
this.gender = gender;
this.greet = function() {
console.log('greet');
};
}
speak() {
console.log("parent speak")
}
static speak() {
console.log("static speak")
}
}
//class 子类 extends 父类
class Son extends Parent {
//在子类的构造方法中调用父类的构造方法
constructor(name, gender, hobby) {
super(name, gender);
this.hobby = hobby;
}
//子类中声明的方法名和父类中的方法名相同时,子类中的方法将覆盖继承于父类的方法
speak() {
console.log("Son speak");
}
}
const grandson = new Son('lucky', 'male', 'reading');
console.log(grandson.name, grandson.gender, grandson.hobby); //lucky male reading
grandson.greet(); //greet
grandson.speak(); //Son speak
Son.speak(); //static speak