• Home
  • Archives
  Evan的博客
  • Home
  • Archives
  • 面试
  • 原理笔记
  • 项目实践
  • 其他

JS 中的类与继承

2020/10/10 posted in  面试

这篇文章主要介绍 JS 面向对象的一些概念。很多人觉得前端面向对象没什么用,其实不然。如果你觉得前端就是写写页面写写样式,那是没什么用的。但是一但涉及到项目架构,或者封装什么库或者 SDK 之类的,面向对象就很有帮助了。

组合继承

组合继承指的是将 原型继承 和 构造函数继承 结合起来

  • 原型继承即 Sub.prototype = new Super(),这种继承的确能继承父类实例(this)和原型(prototype)上的方法,但是如果父类实例中有引用类型的数据结构,那这个继承复制下来的其实是引用类型的指针。这样多个子类的实例其实是共享一个引用数据类型的,修改实例 a 的引用类型,实例 b 的也会受到影响。此外,由于改了 Sub.prototype,导致子类原型链上的 constructor 指向丢失,会导致子类实例找不到子类的原型。
  • 构造函数继承即 Super.call(this, arguments),这种继承相当于把父类的实例(父类 return 的对象)复制给子类。这样能解决不共享引用数据类型的问题,但是这样也没法继承父类原型上的方法。并且每个子类都会有父类实例的副本,这些是子类不需要的。
// 简单的原型继承,缺点是继承父类函数的时候调用了父类的构造函数(子类原型指向父类实例)
// 使得子类原型身上带有很多不需要的父类属性
// 注意:子类原型需要先指向父类实例,才能在子类原型上定义其他

// 定义父类与父类原型
function SuperClass(name) {this.name = name}
SuperClass.prototype.showName = function() {console.log('name', this.name)
}

// 定义子类且调用一次父类,子类原型继承,定义子类原型上的其他方法,这里顺序不能乱
function SubClass(name, age) {SuperClass.call(this, name)
  this.age = age
}
SubClass.prototype = new SuperClass()SubClass.prototype.showAge = function() {console.log('age', this.age)
}

// 测试
const subClass = new SubClass('Evan', 21)
subClass.showName()subClass.showAge()

// 需要注意的是,子类原型指向父类实例之后,子类原型的 constructor 就不能正确找到子类构造函数了
// 因为原型上有一个 constructor 方法,它能让原型找到构造函数,也就是 Fn.prototype.constructor = Fn
// 但是子类原型也就是 SubClass.prototype = new SuperClass() 之后,SubClass.prototype.constructor 就丢失了
// 需要重写 constructor 让这老铁能正确找回自己的构造函数

// 第一个 log 通过,第二个不通过
const superClass = new SuperClass('Van')
console.log(SuperClass.prototype.constructor === SuperClass)
console.log(SubClass.prototype.constructor === SubClass)

// 为了解决 constructor 丢失的问题,我们需要重写 constructor
Object.defineProperty(SubClass.prototype, 'constructor', {
  enumerable: false,
  value: SubClass,
  writable: true
})

// 重新跑一次,指向正常了
console.log(SubClass.prototype.constructor === SubClass)

// instanceof 检查右边的函数原型是否存在于操作符左边的对象的原型链上
console.log(subClass instanceof SubClass)
console.log(subClass instanceof SuperClass)

// 如果把构造函数的原型修改了,instanceof 就会失效
SubClass.prototype = {}
console.log(subClass instanceof SubClass)
console.log(subClass instanceof SuperClass)

寄生组合继承

// 解决组合继承时,子类带有父类构造函数上多余属性的问题
// 关键在于让子类的原型不再指向父类实例,而是指向一个有父类原型方法的对象
// 需要注意的是,这个对象的构造函数(constructor)需要指回子类
function SuperClass(name) {this.name = name}
SuperClass.prototype.showName = function() {console.log('name:', this.name)
}

function SubClass(name, age) {SuperClass.call(this, name)
  this.age = age
}

// 这里顺便把丢失的 constructor 也加上
SubClass.prototype = Object.create(SuperClass.prototype, {
  constructor: {
    value: SubClass,
    writable: true,
    enumerable: false,
    configurable: true
  }
})SubClass.prototype.showAge = function() {console.log('age:', this.age)
}

const sub = new SubClass('Van', 21)
sub.showName()sub.showAge()

类继承

//es6 的类继承,所有的属性都写在 constructor 中
// 继承者需要在 constructor 中 super 声明
class SuperClass {constructor(name) {this.name = name}
  showName() {console.log(this.name)
  }
}

class SubClass extends SuperClass {constructor(name, age) {super(name)
    this.age = age
  }
  showAge() {console.log(this.age)
  }
}

let subClass = new SubClass('Evan', 21)
subClass.showName()subClass.showAge()

« JS 常问手写题汇总

性能优化 »

Evan的博客

Evan 的博客 - 非典型码农,bug永动机
Instagram Weibo GitHub Email RSS

Categories

面试 原理笔记 项目实践 其他 JS Vue 性能优化 算法 计算机网络

Recent Posts

  • 从 HTTP 发展历程重学计算机网络
  • 应届前端的逆袭(中)
  • 应届前端的逆袭(上)
  • 应届前端的逆袭(下)
  • 前端面经复盘

Copyright © 2015 Powered by MWeb,  Theme used GitHub CSS.