javascript继承

继承分类

image

  1. 基于原型链继承(把子类的prototype设置为父类的实例)
    1
    2
    3
    4
    5
    6
    7
    8
    // 父类
    function Person() {}

    // 子类
    function Student(){}

    // 继承
    Student.prototype = new Person()

原型继承的缺点:如果属性是引用类型的话,会共享引用类型(引用属性会被所有实例共享)

1
2
3
4
5
6
7
8
9
10
// 父类
function Person() {
this.hobbies = ['music','reading']
}

// 子类
function Student(){}

// 继承
Student.prototype = new Person()

1
2
3
4
5
6
7
var stu1 = new Student()
var stu2 = new Student()

stu1.hobbies.push('basketball')

console.log(stu1.hobbies) // music,reading,basketball
console.log(stu2.hobbies) // music,reading,basketball
  1. 构造函数继承(在子类中执行父类的构造函数)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    // 父类
    function Person() {
    this.hobbies = ['muisc', 'writing', 'reading']
    }

    // 子类
    function Student(){
    Person.call(this) // 改动的代码
    }
1
2
3
4
5
var stu1 = new Student()
var stu2 = new Student()
stud1.hobbies.push('swimming')
console.log(stud1.hobbies) // ['muisc', 'writing', 'reading', 'swimming']
console.log(stud2.hobbies) // ['muisc', 'writing', 'reading']

构造函数解决了引用类型被共享的问题,但是同时也导致了一个新的问题,每实例化一个子类,就复制了一次父类。

继承方式继承核心代码优缺点
原型链继承Student.prototype = new Person()实例的引用类型共享
构造函数继承在子类(Student)里执行 Person.call(this)实例的引用类型不共享
  1. 组合继承(普通函数使用构造函数继承,函数使用原型链继承)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 父类
function Person() {
this.hobbies = ['music','reading']
}

// 父类函数
Person.prototype.say = function() {console.log('I am a person')}

// 子类
function Student(){
Person.call(this) // 构造函数继承(继承属性) (第二次调用)
}
// 继承
Student.prototype = new Person() // 原型链继承(继承方法) (第一次调用)
1
2
3
4
5
6
7
8
9
// 实例化
var stu1 = new Student()
var stu2 = new Student()

stu1.hobbies.push('basketball')
console.log(stu1.hobbies) // music,reading,basketball
console.log(stu2.hobbies) // music,reading

console.log(stu1.say == stu2.say) // true

优点:

  • 父类的方法可以被复用
  • 父类的引用属性不会被共享
  • 子类构建实例时可以向父类传递参数

缺点:

  • 调用了两次父类的构造函数,第二次调用覆盖了第一次调用的同名参数。这种被覆盖的情况造成了性能上的浪费。
  1. 寄生式继承
    核心:使用原型式继承获得一个目标对象的浅复制,然后增强这个浅复制的能力
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function createAnother(original){ 
var clone=object(original); //通过调用函数创建一个新对象
clone.sayHi = function(){ //以某种方式来增强这个对象
alert("hi");
};
return clone; //返回这个对象
}

var person = {
name: "Nicholas",
friends: ["Shelby", "Court", "Van"]
};

var anotherPerson = createAnother(person);
anotherPerson.sayHi(); //"hi"
  1. 寄生组合继承
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function inheritPrototype(subType, superType){
var prototype = object(superType.prototype); // 创建了父类原型的浅复制
prototype.constructor = subType; // 修正原型的构造函数
subType.prototype = prototype; // 将子类的原型替换为这个原型
}

function SuperType(name){
this.name = name;
this.colors = ["red", "blue", "green"];
}

SuperType.prototype.sayName = function(){
alert(this.name);
};

function SubType(name, age){
SuperType.call(this, name);
this.age = age;
}
// 核心:因为是对父类原型的复制,所以不包含父类的构造函数,也就不会调用两次父类的构造函数造成浪费
inheritPrototype(SubType, SuperType);
SubType.prototype.sayAge = function(){
alert(this.age);
}
  1. ES6 class extends
    核心:es6的继承结果和寄生组合继承相似,但是,寄生组合继承是先创建子类实例this对象,然后再对其进行加强;而es6先将父类实例对象的属性和方法,加到this上面,然后又再使用子类的构造函数修改this
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class A {
}

class B {
}

Object.setPrototypeOf = function (obj, proto) {
obj.__proto__ = proto;
return obj;
}

// B 的实例继承 A 的实例
Object.setPrototypeOf(B.prototype, A.prototype);

// B 继承 A 的静态属性
Object.setPrototypeOf(B, A);

https://juejin.im/post/58f94c9bb123db411953691b