Skip to content

javascript继承

前置知识

原型链就是沿着隐式原型__proto__向上查找属性,一直到Object.prototype.__proto__(等价于null)为止。

__proto__:指向构造函数的原型对象

prototype:指向该方法的原型对象

函数的__proto__指向Function.prototype;实例对象的__proto__指向构造函数的原型对象。


1、原型链继承

核心: 重写原型对象

js
function SuperType() {
    this.a = 1
}
SuperType.prototype.getA = function () {
    return this.a
}
function SubType() {
    this.b = 2
}
SubType.prototype = new SuperType() //  可以继承父类的属性与原型方法
let sub = new SubType()
console.log(sub.a) // 1
console.log(sub.b) // 2 先在本身找,找不到去原型对象上找
console.log(sub.getA()) // 1

缺点: 1.不能给父类传递参数;2.属性与方法【引用类型的值】被所有实例共享。

2、借用构造函数继承

核心:在子类中使用call/apply调用父类

js
function SuperType() {
    this.a = 1
}
SuperType.prototype.getA = function () {
    return this.a
}
function SubType() {
    SuperType.call(this) // 改变父类的this指向
}
let sub = new SubType()
console.log(sub.a) // 1
console.log(sub.getA()) // 报错

缺点: 1. 只能继承属性,不能继承原型方法 2. 每个子类实例都会创建一次,浪费性能

3、组合继承【组合上述两种方法】

js
function SuperType() {
    this.arr = [1, 2, 3]
}
SuperType.prototype.getArr = function () {
    return this.arr
}

function SubType() {
    SuperType.call(this) // 继承方法【每次调用生成新数据,不会有引用类型的问题】
}
SubType.prototype = new SuperType() // 只继承方法【实际是会继承属性的,但是实例访问时会先访问到本身】

let sub1 = new SubType()
sub1.arr.push(4)
console.log(sub1.arr) // [ 1, 2, 3, 4 ]
let sub2 = new SubType()
console.log(sub2.arr) // [ 1, 2, 3 ]

缺点:SuperType在处理继承问题的时候调用了两次,性能损耗。

4、寄生式继承

核心: 使用Objcreat.create或者原生实现的方法进行继承原型

js
function SuperType() {
    this.arr = [1, 2, 3]
}
SuperType.prototype.getArr = function () {
    return this.arr
}
let sub = Object.create(SuperType) // 创建一个对象并将该对象的隐式原型__proto__指向SuperType【注意:该对象非构造函数】
sub.spreadMethod = () => {} // 扩展新方法
console.log(sub.__proto__ === SuperType) // true
console.log(sub.getArr()) // 报错,只能继承父类的静态方法。sub只是一个普通对象,不是函数

5、寄生组合式继承【终极实现方式】=======重头戏

核心: 参考代码注释

js
// 父类
function SuperType() {
    this.name = 'chaos'
    this.arr = [0]
}
SuperType.prototype.getName = function () {
    return '666'
}
// 子类
function SubType() {
    SuperType.call(this) // 一、继承属性。且不存在引用关系
}
// SubType.prototype.selfMethod = () => console.log('self') // 不能在这写,否则会被覆盖掉

// 二、这种继承方案(包括1和2)主要是用来继承原型方法且可自身扩展,并且不需要new第二次SuperType了。
// 继承方案1
// let tempObj = Object.create(SuperType.prototype) // 新建对象&指向父类原型对象(本身扩展方法不会影响到父类原型,并且可以直接调用父类原型对象的方法了)
// SubType.prototype = tempObj

// 继承方案2【就是实现了一下Object.create方法】
function extend(SuperType, SubType) {
    let fn = {} // 或者是let fn = function() {}
    fn.arr2 = []
    fn.__proto__ = SuperType.prototype // 继承原型方法【注意是__proto__】
    SubType.prototype = fn
}
extend(SuperType, SubType) // 继承
SubType.prototype.selfMethod = () => console.log('self') // 子类新的原型方法得在这里写,否则会被第25行覆盖掉

let sub1 = new SubType()
sub1.arr.push(1)
sub1.arr2.push(2)
console.log(sub1.arr) // [ 0, 1 ] 父类继承过来的在本身
console.log(sub1.arr2) // [ 2 ] 寄生继承过来的
console.log('getName', sub1.selfMethod()) // chaos // 父类的原型方法也继承过来了

let sub2 = new SubType()
console.log(sub2.arr) // [ 0 ] // 实例之间互不影响