【个人笔记】JavaScript继承方式

参考文章

本文会在参考文章的基础上拓展内容和补充原型链图解

原型链继承

js 复制代码
function Animal() {  
    this.colors = ['black', 'white']  
}  
Animal.prototype.getColor = function() {  
    return this.colors  
}  
function Dog() {}  
Dog.prototype =  new Animal()  
  
let dog1 = new Dog()  
dog1.colors.push('brown')  
let dog2 = new Dog()  
console.log(dog2.colors)  // ['black', 'white', 'brown']

原型链继承存在的问题:

  • 问题1:原型中包含的引用类型属性将被所有实例共享;
  • 问题2:子类在实例化的时候不能给父类构造函数传参;

原型链继承将派生类的原型对象修改为超类实例,通过原型链可以访问到超类实例和超类原型对象,从而实现继承。

问题一中为什么特别指明"引用类型属性"呢?因为当我们尝试对基本类型属性进行赋值的时候,会自动在实例上创建一个新属性。而引用类型属性如果不是直接赋值,就会修改原型链上的属性,从而影响所有实例。

js 复制代码
dog.age = 5 // 在 dog 实例上新增 age 属性
dog.colors.push = 'brown' // 修改了原型链上共享的 colors 数组
dog.colors = ['brown'] // 给 dog 实例添加了一个新的 colors 属性

借用构造函数实现继承

js 复制代码
function Animal(name) {  
    this.name = name  
    this.getName = function() {  
        return this.name  
    }  
}  
function Dog(name) {  
    Animal.call(this, name)  
}
// 原文还修改了prototype,应该是属于组合继承,这里先不讨论
// Dog.prototype =  new Animal()

借用构造函数实现继承解决了原型链继承的 2 个问题:引用类型共享问题以及传参问题。但是由于方法必须定义在构造函数中,所以会导致每次创建派生类实例都会创建一遍方法。


Animal.call(this, name) 会把 Animal 构造函数的所有实例属性(无论是基本类型还是引用类型),拷贝到当前 Dog 实例上。实现了对超类的实例属性的继承,但无法访问到超类的原型对象上的方法。

组合继承

组合继承结合了原型链和盗用构造函数,将两者的优点集中了起来。基本的思路是使用原型链继承原型上的属性和方法,而通过盗用构造函数继承实例属性。这样既可以把方法定义在原型上以实现重用,又可以让每个实例都有自己的属性。

js 复制代码
function Animal(name) {  
    this.name = name  
    this.colors = ['black', 'white']  
}  
Animal.prototype.getName = function() {  
    return this.name  
}  
function Dog(name, age) {  
    Animal.call(this, name)  // 第二次调用Animal()
    this.age = age  
}  
Dog.prototype =  new Animal()  // 第一次调用Animal()
Dog.prototype.constructor = Dog  
  
let dog1 = new Dog('奶昔', 2)  
dog1.colors.push('brown')  
let dog2 = new Dog('哈赤', 1)  
console.log(dog2)   
// { name: "哈赤", colors: ["black", "white"], age: 1 }

通过Animal.call(this, name)继承超类的实例属性,通过Dog.prototype = new Animal()访问到超类的原型属性和方法。

组合继承的缺点在于两次调用超类的构造函数,Dog.prototype = new Animal()是第一次调用,派生类的原型对象会创建两个属性 name 和 colors。Animal.call(this, name)是第二次调用,在派生类的实例上创建了实例属性name 和 colors,这两个属性屏蔽了原型中的两个同名属性。

简单来说,Dog.prototype = new Animal() 创建了一个多余的超类实例(即上图中间的Animal实例),派生类访问不到这个实例的属性,但又需要它以继承原型上的方法。

寄生式组合继承

js 复制代码
// 方法一、封装
function inheritPrototype(subType, superType){
    var protoType = Object.create(superType.prototype);    
    protoType.constructor = subType;                   
    subType.prototype = protoType;                        
}
inheritPrototype(Dog, Animal)

// 方法二、懒得封装
function Dog(name, age) {  
    Animal.call(this, name)  
}  
Dog.prototype =  Object.create(Animal.prototype) // 在组合继承基础上修改这一行
Dog.prototype.constructor = Dog  

Object.create(superType.prototype)创建一个新对象,使其__proto__指向superType.prototype,避免多次调用超类构造函数。

class实现继承

js 复制代码
class Animal {  
    constructor(name) {  
        this.name = name  
    }   
    getName() {  
        return this.name  
    }  
}  
class Dog extends Animal {  
    constructor(name, age) {  
        super(name)  
        this.age = age  
    }  
}
相关推荐
problc1 分钟前
Pretext —— 无 DOM 文本测量与布局引擎
前端·ai
阿kun要赚马内3 分钟前
Python面向对象:@property装饰器
开发语言·前端·python
徒 花5 分钟前
web前端技术知识复习
前端·html·web
意法半导体STM3217 分钟前
【官方原创】STM32H7双核芯片通过 STlink连接失败问题分析 LAT1654
开发语言·前端·javascript·stm32·单片机·嵌入式硬件
小王C语言18 分钟前
【基础IO】————简单设计一下libc库
前端·数据结构·算法
雨雨雨雨雨别下啦30 分钟前
Vue3——RabbitShopping
前端·javascript·vue.js
BumBle35 分钟前
从声明式到命令式:Vue3 弹窗组件的工厂模式重构
前端
恋猫de小郭41 分钟前
你的蓝牙设备可能正在泄漏你的隐私? Bluehood 如何追踪附近设备并做隐私分析
android·前端·ios
取名不易1 小时前
vue-drawer-board 简单的画图功能
前端
学习指针路上的小学渣2 小时前
JavaScript笔记
前端·javascript