记录下JavaScript 的几种继承方式的理解

本文详细介绍了JavaScript中的多种继承方式,包括原型链继承、构造函数继承、组合继承等,并对比了它们的特点与适用场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


因为最近看到了js的几种继承方式,根据自己理解记录下来了

原型链继承

原型链继承就是将父元素的实例绑定到子元素的原型链上。这样子元素就继承了父元素和父元素原型链上的属性。

function Parent () {
   this.a = 'a'
    this.fn = function () {
        console.log('fn')
    }
}
Parent.prototype.arr = [1, 2, 3]

function Children () {
    this.b = 'b'
}
// 继承之前添加的属性
Children.prototype.c = 'c'
// 原型链继承
Children.prototype = new Parent()

let children = new Children()
for (const key in children) {
    console.log(key)
    // 输出结果: b、a、fn、arr
    // 可以看到我们在继承之前添加的属性 c 被覆盖了
}
console.log(children.arr)  // [1, 2, 3]
children.arr.push(4)
let children2 = new Children()
// 可以看到子类实例修改从父类继承过来的引用属性会影响到所有的子类实例
console.log(children2.arr) // [1, 2, 3, 4]
console.log(children.arr) // [1, 2, 3, 4]
// 父类的引用属性也被修改了
console.log(new Parent().arr) // [1, 2, 3, 4]
// 父类新增属性,子类也可以访问
Parent.prototype.d = 'd'
console.log(`${children.d}-${children2.d}`) // 'd-d'

特点:

  1. 继承单一,不能给父类构造函数传递参数
  2. 在继承的时候会覆盖子类继承之前的属性,但是不会覆盖继承之后的属性
  3. 父类如果新增了属性,子类可以访问到
  4. 子类修改继承过来的父类引用属性会导致父类引用属性被修改
  5. 父类的属性会被子类所有实例共享

原型式继承

原型式继承就是 新建一个空的构造函数,将父类的实例绑定到这个空的构造函数中在返回这个构造函数,从而达到继承的效果

function Parent (...args) {
    // 子类传递过来的参数 
    console.log(...args) // 1, 3, 4
    // 父类本身的属性
    this.a = 'a'
    this.fnc = function () {
        console.log('fnc')
    }
    this.arr = [1, 2, 3]
}
// 父类原型链上的方法
Parent.prototype.publicFunction = function () {
    console.log(this.a)
}
/**
 * @params { Function } parent 是父类实例
 * @returns: Function 返回一个新的继承于 Parent 的构造函数
 */
function createClass (parent) {
	// 新建一个空的函数
	let fnc = function () {}
	// 将当前函数的 prototype 指向 父类实例(这里就是原型链继承)
	fnc.prototype = parent
	return new fnc()
}
// 新生成的 children 就是继承与 父类的一个实例
let children = createClass(new Parent())
console.log(children instanceof Parent) // true
console.log(children.a) // 'a'
console.log(children.publicFunction()) // 'a'

特点:

  1. 这玩意继承和 原型链继承 一样
  2. es5新增了个 Object.create(obj, [propertiesObject]) 返回一个对象。这个对象继承与 obj

构造函数继承

通过call、apply 调用父类的构造函数并且修改父类构造函数的 this 指向 达到继承效果

function Parent (...args) {
    // 子类传递过来的参数 
    console.log(...args) // 1, 3, 4
    // 父类本身的属性
    this.a = 'a'
    this.fnc = function () {
        console.log('fnc')
    }
    this.arr = [1, 2, 3]
}
// 父类原型链上的属性
Parent.prototype.b = 'b'

function Children () {
    // 通过构造修改构造函数 Parent 的 this 指向,实现继承
    // 可以向父类传递参数
    let args = arguments
    Parent.call(this, ...args)
    // Parent.apply(this, args)
    // 这里可以继承多个父类
    // Parent2.call(this)
    this.c = 'c'
}
// 给父类传递参数
let children = new Children(1, 3, 4)
for (const key in children) {
    console.log(key)
    // 输出结果: a、fnc、c
    // 可以看到没有没有父类在原型链上添加的属性 b
}
console.log(children.b) // undefined

children.arr.push(4)
let children2 = new Children()
// 可以看到修改父类继承过来的引用属性不会导致其他实例/父类被修改
console.log(children.arr) // [1, 2, 3, 4]
console.log(children2.arr) // [1, 2, 3]
console.log(new Parent().arr) // [1, 2, 3]
// 只是子类的实例,不是父类的实例 而原型链继承是
console.log(children instanceof Parent) // false

特点:

  1. 无法继承父类通过 prototype 增加的属性(原型链上的属性),只能继承实例属性
  2. 解决了父类引用属性被多个子类实例共享
  3. 可以给父类传递参数
  4. 可以继承多个父类
  5. 只是子类的实例,不是父类的实例
  6. 无法实现方法的复用,因为每次需要继承的方法都只能写在父类里面

组合继承

组合继承就是 原型链继承 + 构造函数继承。简单来说就是:通过构造函数继承父类的实例方法/属性,通过原型链继承父类的原型方法/属性
注意:需要将子类的构造函数指向子类,否则会指向父类构造函数

function Parent (...args) {
    // 子类传递过来的参数 
    console.log(...args) // 1, 3, 4
    // 父类本身的属性
    this.a = 'a'
    this.fnc = function () {
        console.log('fnc')
    }
    this.arr = [1, 2, 3]
}
// 父类原型链上的方法
Parent.prototype.publicFunction = function () {
    console.log(this.a)
}

function Children () {
    // 通过构造修改构造函数 Parent 的 this 指向,实现继承
    // 可以像父类传递参数
    let args = arguments
    Parent.call(this, ...args)
    // Parent.apply(this, args)
    this.c = 'c'
}

// 1、将父类的实例挂载到子类的原型链上
Children.prototype = new Parent()

// 2、将父类的 prototype 复制给子类的 prototype (寄生式继承)
// Children.prototype = Parent.prototype

// 需要修改子类的构造函数的指向 否则会指向 父类的构造函数
Children.prototype.constructor = Children

// 给父类传递参数
let children = new Children(1, 3, 4)
for (const key in children) {
    console.log(key)
    // 输出结果: a、fnc、c
    // 可以看到没有没有父类在原型链上添加的属性 b
}
console.log(children.b) // undefined

children.arr.push(4)
let children2 = new Children()
// 可以看到修改父类继承过来的引用属性不会导致其他实例/父类被修改
console.log(children.arr) // [1, 2, 3, 4]
console.log(children2.arr) // [1, 2, 3]
console.log(new Parent().arr) // [1, 2, 3]

// 子类实例是父类的实例
console.log(children instanceof Parent) // true

特点:

  1. 组合继承结合了 原型链继承 和 构造函数继承
  2. 实现了函数的复用。
  3. 子类可以调用父类原型链上的属性/方法
  4. 子类实例是父类的实例
  5. 调用了两次父类的构造函数,所以导致子类原型上有两份父类的实例属性/方法
    将父类的 prototype 复制给子类的 prototype。这样可以避免调用了两次父类的构造函数导,子类原型上也不会有两份父类的实例方法(这就是寄生式继承

拷贝继承

就是将父类的 prototype 上的属性 复制到子类上

function Parent (...args) {
    // 子类传递过来的参数 
    console.log(...args) // 1, 3, 4
    // 父类本身的属性
    this.a = 'a'
    this.fnc = function () {
        console.log('fnc')
    }
    this.arr = [1, 2, 3]
}
// 父类原型链上的方法
Parent.prototype.publicFunction = function () {
    console.log(this.a)
}

// 通过 for in 遍历父类来 copy 属性实现继承
/**
 * @params { Function } parent 是父类实例
 * @returns: Void
 */
function CreateClass (parent) {
	for (const key in parent) {
        this[key] = parent[key]
    }
}
let children = new CreateClass(new Parent())
console.log(children.a) // a

特点:

  1. 可以实现多继承
  2. copy属性效率低
  3. 无法获取父类不可枚举对象 enumerable: false

还看到一种寄生式继承,但是这种寄生式继承方式于组合继承的区别就在于 在给子类添加父类的prototype属性的方式。已经在组合继承中写了通过修改 prototype 的方式来实现继承父类的 原型属性

es6 extends 继承

最后简单记录下 es6 类的继承
注意:class 不存在变量提升。所以必须先定义在调用

// 新建一个动物类
class Animal {
    // 属性
    food = ''
    // 构造函数
    constructor (food) {
        this.food = food
    }
    eat () {
        console.log(`吃${this.food}`)
    }
    // 静态方法直接调用
    static hellow () {
        console.log('hellow wold')
    }
}
// 使用关键字 extends
// Dog 类 继承于 Animal
class Dog extends Animal {
    constructor (food) {
        // 给父类构造函数传递参数
        super(food)
    }
    run () {
        console.log('dog can run')
    }
}

let dog = new Dog('小黄')
dog.run()
// 不会继承父类的静态方法
// dog.hellow() // dog.hellow is not a function
Animal.hellow()
dog.eat()

特点:

  1. 这个和其他语言的继承差不多
  2. 没有 provide 关键字。可以通过函数包裹模拟达到
  3. 可以实现多继承,使用 mixin 模式实现多继承。文档链接
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值