JS高级ES6的6种继承方式

目录
  • 1.原型链继承
  • 2.借助构造函数继承
  • 3.组合式继承
  • 4.原型式继承
  • 5.寄生式继承
  • 6.寄生组合式继承

前言:

继承是面向对象中老生常谈的一个内容,在ECMAScript6之前,JavaScript中的继承可谓是非常的繁琐的,有各种各样的继承,本质上所有的继承都是离不开原型链的,ES6新增的extends关键字也是通过原型链实现的继承,但是语法相对来说就简单了很多。

关于原型链的内容,可以参考上篇文章两张图搞懂原型链

本篇文章就来介绍一下在ECMAScript6之前是怎么实现继承的。

1.原型链继承

借助于原型链继承本质就是修改一下原型的指向即可,实现代码如下:

function ParentClass() {
  this.name = '一碗周'
}
ParentClass.prototype.getName = function () {
  return this.name
}

// 定义子类,将来用于继承父类
function ChildClass() {}

// * 将子类的原型指向父类的实例化,子类拥有父类实例化后的内容
ChildClass.prototype = new ParentClass()

// 将子类进行实例化
var child = new ChildClass()

console.log(child.getName()) // 一碗周

上面的代码图解如下:

图中红色线表示这个构造函数与实例对象的原型链,通过这个原型链的关系,从而实现了继承。

这种方式实现继承有一个缺点就是多个实例会导致原型对象上的内容时共享的,内容之间会互相影响,测试代码如下:

function ParentClass() {
  this.colors = ['red', 'blue', 'green']
}
function ChildClass() {}

ChildClass.prototype = new ParentClass()

var child1 = new ChildClass()
var child2 = new ChildClass()
console.log(child1.colors) // [ 'red', 'blue', 'green' ]

child2.colors.push('black')
console.log(child2.colors) // [ 'red', 'blue', 'green', 'black' ]

console.log(child1.colors) // [ 'red', 'blue', 'green', 'black' ]

测试代码中的child1并没有进行修改,但是修改了child1之后,child1中的值也发生了改变。

2.借助构造函数继承

所谓的借助构造函数继承(有些资料也称为伪造对象或经典继承),就是通过子对象借助Function.call()或者Function.apply()方法调用父类构造函数完成继承,

示例代码如下所示:

function Parent() {
  // 父级对象

  this.parent = 'parent'
}

Parent.prototype.name = '一碗周' // 为 Parent 父级对象的原型增加属性

function Child() {
  // 子级对象

  this.child = 'child'

  Parent.call(this) // 使用 call() 或者 apply() 方法调用父级构造函数 实现继承。
}

const child = new Child()

console.log(child)

console.log(child.name) // undefined     // 不会继承父类的原型

执行流程如下所示:

使用这种方式的优点是避免了引用类型的实例被所有对象共享,缺点是因为所有的方法都定义在了构造函数中,是不会继承原型对象,而且每实例化一个对象之后都会重新创建一遍这些方法,占用内存空间,更别说函数复用了。

3.组合式继承

之前掌握的两种继承方式都是存在缺点的,基于原型继承的继承方式,所有实例化后的对象都共享原型的方法和属性,如果有一个更改则都会进行更改。而借助构造函数继承的方式又无法继承原型属性。所以就出现了结合式继承,就是将基于原型继承方式和借助构造函数的继承方式结合起来,取其精华去其糟粕的一种继承方式。

实现组合式继承的基本思路如下:

  • 使用原型链或原型式继承实现对原型的属性和方法的继承。
  • 通过结构构造函数实现对实例对象的属性的继承。

这样,既通过在原型上定义方法实现了函数的复用,又可以保证每个对象都有自己的专有属性。

示例代码如下所示:

// 父级对象
function Parent() {
  this.parent = 'parent'
}
// 为 Parent 父级对象的原型增加属性
Parent.prototype.name = '一碗周'
// 子级对象
function Child() {
  this.child = 'child'
  // 使用 call() 或者 apply() 方法调用父级构造函数 实现继承。
  Parent.call(this)
}
// 解决不会继承构造函数的原型对象的问题
Child.prototype = Parent.prototype

const child = new Child()
console.log(child.name) // 一碗周

4.原型式继承

我们可以使用Object.create()方法实现一种继承,实例代码如下:

var person = {
  name: '一碗周',
  friends: ['张三', '李四', '王五'],
}

var anotherPerson = Object.create(person)
anotherPerson.name = '一碗甜'
anotherPerson.friends.push('赵六')

console.log(person.friends) // [ '张三', '李四', '王五', '赵六' ]

该方式的缺点与第一种一样,都是多个实例会导致原型对象上的内容时共享的,内容之间会互相影响。

5.寄生式继承

寄生式继承的基础是在原型式继承的的基础上,增强对象,返回构造函数,实例代码如下:

var person = {
  name: '一碗周',
  friends: ['张三', '李四', '王五'],
}

function createAnother(original) {
  var clone = Object.create(original) // 通过调用 object() 函数创建一个新对象
  clone.sayMe = function () {
    // 以某种方式来增强对象
  }
  return clone // 返回这个对象
}

var anotherPerson = createAnother(person)
anotherPerson.sayMe()

它的缺点与原生式继承是一样的。

6.寄生组合式继承

该继承方式是借助构造函数传递参数和寄生式继承所实现的,实例代码如下:

function inheritPrototype(ChildClass, ParentClass) {
  var prototype = Object.create(ParentClass.prototype) // 创建对象,创建父类原型的一个副本
  // 修改创建的父类原型副本的 constructor 并将子类的 prototype 指向这个类,形成与父类无关联的类
  prototype.constructor = ChildClass
  ChildClass.prototype = prototype
}

// 父类初始化实例属性和原型属性
function ParentClass(name) {
  this.name = name
  this.colors = ['red', 'blue', 'green']
}
ParentClass.prototype.sayName = function () {
  console.log(this.name)
}

// 借用构造函数传递增强子类实例属性(支持传参和避免篡改)
function ChildClass(name, age) {
  // 拷贝父类所有自有属性
  ParentClass.call(this, name)
  this.age = age
}

// 将父类原型指向子类
inheritPrototype(ChildClass, ParentClass)

// 新增子类原型属性
ChildClass.prototype.sayAge = function () {
  console.log(this.age)
}

var instance1 = new ChildClass('一碗周', 19)
var instance2 = new ChildClass('一碗甜', 18)

instance1.colors.push('black')
console.log(instance1.colors) // [ 'red', 'blue', 'green', 'black' ]
instance1.sayName() // 一碗周
instance2.colors.push('yellow')
console.log(instance2.colors) // [ 'red', 'blue', 'green', 'yellow' ]

这个例子的高效率体现在它只调用了一次ParentClass构造函数,并且因此避免了在ChildClass.prototype上创建不必要的、多余的属性。于此同时,原型链还能保持不变;因此,还能够正常使用instanceofisPrototypeOf()。

如果你没有看懂,那就继续看,首先,我们将核心代码进行抽离,如下图:

上图中就是我们的核心代码,然后我们来看一下默认的ParentClassChildClass的原型链是什么样子的,

图如下:

然后我们调用inheritPrototype()方法,并将修改ChildClass的原型,解析图如下:

最后不要忘记了在子类中通过call()方法调用父类,从而实现copy父类的自有属性,至此就实现了一个比较完善的继承方式。

结语:

这篇文章介绍了除extends关键字之外的六种继承方式,虽然说在ECMAScript6中新增了class关键字以及类相关的所有内容,文本介绍的继承方式都已经不怎么使用了。

但是ECMAScript6新增的类本质上就是语法糖,只要是JavaScript谈论继承,始终离不开class关键字。

到此这篇关于JS高级ES6的6种继承方式的文章就介绍到这了,更多相关ES6的6种继承方式内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • ES6 javascript中Class类继承用法实例详解

    本文实例讲述了ES6 javascript中Class类继承用法.分享给大家供大家参考,具体如下: 1. 基本用法 Class 之间可以通过extends关键字实现继承, 这比 ES5 的通过修改原型链实现继承, 要清晰和方便很多. class ColorPoint extends Point {} 上面代码定义了一个ColorPoint类, 该类通过extends关键字, 继承了Point类的所有属性和方法. 但是由于没有部署任何代码, 所以这两个类完全一样, 等于复制了一个Point类. 下

  • java设计模式之观察者模式的介绍及使用

    一.定义 观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象(通知者).这个主题对象观察到被观察者发生变化时,会通知所有的观察者对象,使它们能够自己更新自己 这里涉及了几个角色及他们自己功能: 观察者对象:可以更新自己 主题对象:可以添加观察者,移除观察者,通知观察者 被观察者:被主题对象监视,当被观察者发生变化时,主题对象会通知观察者更新自己的状态 二.使用场景 当一个对象改变需要同时改变其他对象的时候,而且不需要知道有多少个对象需要改变 三.举个例子 如果干巴巴的概

  • JS面向对象编程——ES6 中class的继承用法详解

    本文实例讲述了 ES6 中class的继承用法.分享给大家供大家参考,具体如下: JS是一种基于对象的语言,要实现面向对象,写法跟传统的面向对象有很大的差异.ES6引入了Class语法糖,使得JS的继承更像面向对象语言的写法. 此篇博客,分为:基本介绍.Vue使用案例 基本介绍 Class可以通过extends关键字实现继承,这比ES5的通过修改原型链实现继承,要清晰和方便很多: class Father { } class Son extends Father { } 代码定义了一个Son 类

  • JS高级ES6的6种继承方式

    目录 1.原型链继承 2.借助构造函数继承 3.组合式继承 4.原型式继承 5.寄生式继承 6.寄生组合式继承 前言: 继承是面向对象中老生常谈的一个内容,在ECMAScript6之前,JavaScript中的继承可谓是非常的繁琐的,有各种各样的继承,本质上所有的继承都是离不开原型链的,ES6新增的extends关键字也是通过原型链实现的继承,但是语法相对来说就简单了很多. 关于原型链的内容,可以参考上篇文章两张图搞懂原型链 本篇文章就来介绍一下在ECMAScript6之前是怎么实现继承的. 1

  • js中常见的6种继承方式总结

    目录 前言 1.原型继承 2.盗用构造函数 3.组合继承 4.原型式继承 5.寄生式继承 6.寄生式组合继承 总结 前言 js是门灵活的语言,实现一种功能往往有多种做法,ECMAScript没有明确的继承机制,而是通过模仿实现的,根据js语言的本身的特性,js实现继承有以下通用的几种方式 温馨提示:本文中Super函数都是指父类,Sub函数都是代表子类.同时文中会涉及到“构造函数模式”和“工厂模式”,如果不熟悉的小伙伴,可以先看看这篇介绍 js常见的4种创建对象方式. 1.原型继承 实现: fu

  • 浅谈js中的三种继承方式及其优缺点

    第一种,prototype的方式: //父类 function person(){ this.hair = 'black'; this.eye = 'black'; this.skin = 'yellow'; this.view = function(){ return this.hair + ',' + this.eye + ',' + this.skin; } } //子类 function man(){ this.feature = ['beard','strong']; } man.pr

  • 分享JavaScript 中的几种继承方式

    目录 一.原型链 1.1 原型链的问题 二.盗用构造函数 2.1 基本思想 2.2 可向父类构造函数传参 2.3 盗用构造函数的问题 三.组合继承(伪经典继承) 3.1 基本思想 3.2 组合继承的问题 四.原型式继承 4.1 基本思想 4.2 Object.create() (1)语法 (2)示例 (3)手动实现 五.寄生式继承 5.1 基本思想 5.2 寄生式继承 六.寄生式组合继承 6.1 基本思想 前言: 说到JavaScript中的继承,与之密切相关的就是原型链了,JavaScript

  • JavaScript中常见的几种继承方式

    目录 原型继承 内存图 分析 盗用构造函数继承 分析 组合继承 原型链继承 寄生式继承 寄生组合式继承 原型继承 function Parent(name) { this.name = name } Parent.prototype.getName = function(){ return 'parentPrototype' + ' ' + this.name; } function Son() { } Son.prototype = new Parent(); console.log(new

  • C++ 的三种访问权限与三种继承方式

    三种访问权限 我们知道C++中的类,有三种访问权限(也称作访问控制),它们分别是public.protected.private.要理解它们其实也很容易,看下面了一个例子. 父类: class Person { public: Person(const string& name, int age) : m_name(name), m_age(age) { } void ShowInfo() { cout << "姓名:" << m_name <&l

  • Javascript编程中几种继承方式比较分析

    本文实例分析了Javascript编程中几种继承方式比较.分享给大家供大家参考,具体如下: 开篇 从'严格'意义上说,javascript并不是一门真正的面向对象语言.这种说法原因一般都是觉得javascript作为一门弱类型语言与类似java或c#之类的强型语言的继承方式有很大的区别,因而默认它就是非主流的面向对象方式,甚至竟有很多书将其描述为'非完全面向对象'语言.其实个人觉得,什么方式并不重要,重要的是是否具有面向对象的思想,说javascript不是面向对象语言的,往往都可能没有深入研究

  • Javascript中的几种继承方式对比分析

    开篇 从'严格'意义上说,javascript并不是一门真正的面向对象语言.这种说法原因一般都是觉得javascript作为一门弱类型语言与类似java或c#之类的强型语言的继承方式有很大的区别,因而默认它就是非主流的面向对象方式,甚至竟有很多书将其描述为'非完全面向对象'语言.其实个人觉得,什么方式并不重要,重要的是是否具有面向对象的思想,说javascript不是面向对象语言的,往往都可能没有深入研究过javascript的继承方式,故特撰此文以供交流. 为何需要利用javascript实现

  • 详解C++ 中的三种继承方式

    public 方式继承 基类成员对派生类的可见性对派生类来说,基类的公有成员和保护成员可见,基类的公有成员和保护成员作为派生类的成员时,它们都保持原有的状态;基类的私有成员不可见,基类的私有成员仍然是私有的,派生类不可访问基类中的私有成员. 基类成员对派生类对象的可见性对派生类对象来说,基类的公有成员是可见的,其他成员是不可见的. 所以,在公有继承时,派生类的对象可以访问基类中的公有成员,派生类的成员函数可以访问基类中的公有成员和保护成员. 简单来说,派生类能访问基类的public, prote

  • 一文详解JS私有属性的6种实现方式

    目录 _prop Proxy Symbol WeakMap #prop ts private 总结 class 是创建对象的模版,由一系列属性和方法构成,用于表示对同一概念的数据和操作. 有的属性和方法是对外的,但也有的是只想内部用的,也就是私有的,那怎么实现私有属性和方法呢? 不知道大家会怎么实现,我梳理了下,我大概用过 6 种方式,我们分别来看一下: _prop 区分私有和公有最简单的方式就是加个下划线 _,从命名上来区分. 比如: class Dong { constructor() {

随机推荐