这是我参与「第四届青训营 」笔记创作活动的的第8天
继承:继承允许我们依据另一个类来定义一个类,这使得创建和维护一个应用程序变得更容易。这样做,也达到了重用代码功能和提高执行效率的效果。基类 & 派生类
- 每一个构造函数都有一个原型对象
prototype,原型对象又包含指向构造函数的指针constructor - 构造函数
prototype直接定义的属性,与constructor在一个层级 Parent.prototype === parent.__prototype构造函数的原型对象 === 实例对象的原型- 实例身上有构造函数内部定义的属性和方法,实例原型链上有构造函数prototype上定义的属性和方法
定义一个父类
function Parent(name) {
this.name = name || "Parent";
this.arr = [1, 2, 3];
this.study = function() {
console.log(`${this.name} is studying`);
}
}
Parent.prototype.learn = function() {
console.log(`${this.name} is learning javascript`)
}
console.log(new Parent())
- 实例对象身上的属性和方法未 — 构造函数内部声明的属性和方法为
- 实例对象原型链__proto__上的属性和方法为构造函数prototype上定义的属性和方法
一、原型链继承
过程分析
- 子类(派生类)需要能够访问父类内部的成员变量
- => 父类构造函数的实例对象身上有构造函数内部的成员变量
- => 将子类prototype指向父类构造函数的实例
- => 父类实例中的constructor属性指向父类构造函数
- => 修改子类的constructor指向
代码实现
function Child(){}
Child.prototype.name = 'Child'
Child.prototype = new Parent();
Child.prototype.constructor = Child;
优点
- 方法复用,复用父类定义在原型上的方法,比如study()
缺点
- 创建子类实例的时候不能给父类传值【只能在指定子类prototype的时候给父类传值】
const child = new Child("child");
console.log(child.name) // "Parent"
- 共享父类引用属性【对于父类中引用类型的值,多个子类实例公用一个,一个实例修改,会连带修改其他实例的该属性值】
const child1 = new Child();
const child2 = new Child();
console.log(child1.arr === child2.arr)
child1.arr.push(4)
console.log(child2.arr)
- 不能实现多继承(多继承:可以继承多个类)【子类的prototype只能指向一个值】
二、构造函数继承
过程分析
- 子类(派生类)需要能够访问父类内部的成员变量
- => 利用call方法将父类的this绑定为子类的this
- => 相当于复制父类的实例属性给子类(原型没复制)
代码实现
function Child(name){
Parent.call(this, name);
}
const child = new Child("Tim")
child.study(); // "Tim is studying" 父类构造函数的属性和方法可以继承
child.learn(); // 报错,不会继承父类原型上的方法和属性
优点
- 可以实现多继承,只要在子类构造函数中调用多个父类构造函数即可
缺点
- 只能继承父类实例的属性和方法,不能继承原型上的属性和方法
三、组合继承
过程分析
原型链继承 + 构造函数继承
代码实现
function Child(name){
Parent.call(this, name);
}
Child.prototype = new Parent();
Child.prototype.constructor = Child;
优点
- 既可以继承父类内部的属性和方法,也可以继承原型链上的属性和方法
const child = new Child("Tim")
child.study();
child.learn();
缺点
- 可以继承多个父类构造函数内部的属性和方法,但是只能继承一个父类原型链上的属性和方法
- 调用了两次父类构造函数,生成了两份实例
四、原型式继承
针对某个对象进行的简单继承
- 向函数中传入一个对象,然后返回一个以这个对象为原型的对象
- => 实现Object.create() =>
Object.create(proto, propertiesObject)
五、寄生式继承
【有点含糊,勉强理解】
const person = {
name:'Jack',
friends:['John','Bob']
}
function createAnother(proto){
let clone = Object.create(proto)
//增强对象,添加属于自己的方法
clone.sayHi = function(){
console.log('hi')
}
return clone
}
const person1 = createAnother(person)
const person2 = createAnother(person)
person1.friends.push('Tom')
console.log(person2.friends) // ['John', 'Bob', 'Tom']
person1.sayHi() // hi
多个子类共享的父类属性会相互影响
六、寄生组合继承
- 通过构造一个没有实例属性的类,将该类的prototype指向父类的prototype
- => 不会实例化两次父类构造函数内的属性和方法
function Child(name){
Parent.call(this, name);
}
(function () {
function Super() {}
Super.prototype = Parent.prototype;
Child.prototype = new Super();
})()