博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
JS 总结之原型继承的几种方式
阅读量:6869 次
发布时间:2019-06-26

本文共 3765 字,大约阅读时间需要 12 分钟。

在之前的总结中,我们详细分析了原型,原型很大作用用于模拟继承,这一次,我们来聊原型继承的几种方式。

? 前提

以一个父类为前提条件,列举 js 继承的继承方式:

function Person (age) {  this.age = age || 18}Person.prototype.sleep = function () {  console.log('sleeping')}复制代码

? 方式 1:原型链继承(不推荐)

function Programmer() {}Programmer.prototype = new Person ()Programmer.prototype.code = function () {  console.log('coding')}let jon = new Programmer()jon.code() // codingjon.sleep() // sleepingjon instanceof Person // truejon instanceof Programmer // trueObject.getPrototypeOf(jon) // Person {age: 18, code: ƒ}jon.__proto__ // Person {age: 18, code: ƒ}复制代码

缺点:

  1. 无法向父类构造函数传参
  2. 父类的所有属性被共享,只要一个实例修改了属性,其他所有的子类实例都会被影响

? 方式 2:借用构造函数(经典继承)(不推荐)

复制父类构造函数内的属性

function Programmer(name) {  Person.call(this)  this.name = name}let jon = new Programmer('jon')jon.name // jonjon.age // 18jon.sleep() // Uncaught TypeError: jon.sleep is not a functionjon instanceof Person // falsejon instanceof Programmer // true复制代码

优点:

  1. 可以为父类传参
  2. 避免了共享属性

缺点:

  1. 只是子类的实例,不是父类的实例
  2. 方法都在构造函数中定义,每次创建实例都会创建一遍方法

? 方式 3:组合继承(推荐)

组合 原型链继承借用构造函数继承

function Programmer(age, name) {  Person.call(this, age)  this.name = name}Programmer.prototype = new Person()Programmer.prototype.constructor = Programmer // 修复构造函数指向let jon = new Programmer(18, 'jon')jon.age // 18jon.name // jonlet flash = new Programmer(22, 'flash')flash.age // 22flash.name // flashjon.age // 18jon instanceof Person // truejon instanceof Programmer // trueflash instanceof Person // trueflash instanceof Programmer // true复制代码

优点:融合原型链继承和构造函数的优点,是 JavaScript 中最常用的继承模式

缺点:调用了两次父类构造函数

? 方式 4:原型式继承(不推荐)

function create(o) {  function F() {}  F.prototype = o  return new F()}let obj = {  gift: ['a', 'b']}let jon = create(obj)let xiaoming = create(obj)jon.gift.push('c')xiaoming.gift // ['a', 'b', 'c']复制代码

缺点:共享了属性和方法

? 方式 5:寄生式继承(不推荐)

创建一个仅用于封装继承过程的函数,该函数在内部以某种形式来做增强对象,最后返回对象

function createObj (o) {  var clone = Object.create(o)  clone.sayName = function () {    console.log('hi')  }  return clone}复制代码

缺点:跟借用构造函数模式一样,每次创建对象都会创建一遍方法

? 方式 6:寄生组合继承(最佳)

子类构造函数复制父类的自身属性和方法,子类原型只接受父类的原型属性和方法:

function create(prototype) {  function Super() {}  Super.prototype = prototype  return new Super()}function Programmer(age, name) {  Person.call(this, age)  this.name = name}Programmer.prototype = create(Person.prototype)Programmer.prototype.constructor = Programmer // 修复构造函数指向let jon = new Programmer(18, 'jon')jon.name // jon复制代码

进阶封装:

function create(prototype) {  function Super() {}  Super.prototype = prototype  return new Super()}function prototype(child, parent) {  let prototype = create(parent.prototype)  prototype.constructor = child // 修复构造函数指向  child.prototype = prototype}function Person (age) {  this.age = age || 18}Person.prototype.sleep = function () {  console.log('sleeping')}function Programmer(age, name) {  Person.call(this, age)  this.name = name}prototype(Programmer, Person)let jon = new Programmer(18, 'jon')jon.name // jon复制代码

引用《JavaScript 高级程序设计》中对寄生组合式继承的夸赞就是:

这种方式的高效率体现它只调用了一次 Parent 构造函数,并且因此避免了在 Parent.prototype 上面创建不必要的、多余的属性。与此同时,原型链还能保持不变;因此,还能够正常使用 instanceof 和 isPrototypeOf。开发人员普遍认为寄生组合式继承是引用类型最理想的继承范式。

? 方式 7:ES6 extends(最佳)

// 父类class Person {  constructor(age) {    this.age = age  }  sleep () {    console.log('sleeping')  }}// 子类class Programmer extends Person {  constructor(age, name) {    super(age)    this.name = name  }  code () {    console.log('coding')  }}let jon = new Programmer(18, 'jon')jon.name // jonjon.age // 18let flash = new Programmer(22, 'flash')flash.age // 22flash.name // flashjon instanceof Person // truejon instanceof Programmer // trueflash instanceof Person // trueflash instanceof Programmer // true复制代码

优点:不用手动设置原型。

缺点:新语法,只要部分浏览器支持,需要转为 ES5 代码。

? 参考

  • 《JavaScript 高级程序设计(第 3 版)》6.3 继承
  • by 冴羽
  • by 阮一峰

转载于:https://juejin.im/post/5c1f9fc0f265da6125781973

你可能感兴趣的文章
今天被反馈微信企业号进行操作时出现陌生页面,后查询时遭到流量劫持了
查看>>
loadView、viewDidLoad及viewDidUnload的关系
查看>>
TCP/UDP穿越NAT的P2P通信方法(Hole Punching)
查看>>
django对数据库操作的封装
查看>>
python实现linux下指定目录下文件中的单词个数统计
查看>>
SQL SERVER存储过程中如何使用事务与try catch
查看>>
没什么不可能:剿灭Windows下的29个烦恼
查看>>
String,StringBuffer,StringBuilder的整理
查看>>
我的友情链接
查看>>
nginx + tomcat 配置,静态资源直接使用nginx
查看>>
mysql 字符截取 实列
查看>>
linux中crontable的用法 附件二
查看>>
部署mysql高可用、读写分离集群
查看>>
jquery中下拉多选插件jquery.multiSelect的使用
查看>>
梦想与现实,你会选择什么——一个电子爱好者的迷茫
查看>>
用rabbitMQ实现生产者消费者
查看>>
GRADLE遇见“设备未就绪”
查看>>
正则表达式实现——匹配括号中的A 以及 匹配非括号中的A
查看>>
golang锁sync.Mutex
查看>>
定义自己的JSTL标签库
查看>>