一文弄懂JavaScript的继承方式

目录
  • JavaScript中的继承方式
  • 问:JavaScript中有几种继承方式呢
  • 问:每种继承方式是怎么实现的呢
    • 盗用构造函数
    • 组合继承
    • 原型链式继承
    • 寄生式继承
    • 寄生时组合继承

JavaScript中的继承方式

问:JavaScript中有几种继承方式呢

emmm...六种?五种?还是四种来着...

这次记清楚了 一共有五种继承方式

  • 盗用构造函数 (经典继承方式 )
  • 组合继承
  • 原型链式继承
  • 寄生式继承
  • 寄生式组合继承

问:每种继承方式是怎么实现的呢

盗用构造函数

基本思路很简单:在子类构造函数中调用父类构造函数。因为毕竟函数就是在特定上下文中执行代码的简单对象,所以可以使用apply() 和 call() 方法以新创建的对象为上下文执行构造函数。

盗用构造函数的一个优点就是可以在子类构造函数中向父类构造函数传参

function SuperType(name) {
  this.name = name
  this.sayName = function () {
      console.log(this.name);
  }
}
function SubType(name, age) {
  //  继承 SuperType 并传参
  SuperType.call(this, name);
  //实例属性
  this.age = age
}
let instance1 = new SubType('zxc', 2)
let instance2 = new SuperType('gyx')
// instance1.sayHi() // ?类也不能访问父类原型上定义的方法,因此所有类型只能使用构造函数模式
console.log(instance1);  // SubType { name: 'zxc', sayName: [Function (anonymous)], age: 2 }

组合继承

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

function SuperType(name) {
    this.name = name
    this.colors = ['red', 'blue', 'green']
}
SuperType.prototype.sayName = function () {
    console.log(this.name);
}
function SubType(name, age) {
    //继承实例属性
    SuperType.call(this, name)
    this.age = age
}
//继承原型方法
SubType.prototype = new SuperType()
SubType.prototype.sayAge = function () {
    console.log(this.age);
}
let instance1 = new SubType('zxc', '22')
instance1.sayName()  //zxc
instance1.sayAge()   // 22
let instance2 = new SubType("Greg", 27);
console.log(instance2.colors); // "red,blue,green"
instance2.sayName(); // "Greg";
instance2.sayAge(); // 27

原型链式继承

原型式继承适用于这种情况:你有一个对象,想在它的基础上再创建一个新对象。你需要把这个对象先传给 object() ,然后再对返回的对象进行适当修改。

Object.create() 方法将原型式继承的概念规范化了 这个方法接收两个参数:作为新对象原型的对象,以及给新对象定义额外属性的对象(第二个可选)

let person = {
   name: "Nicholas",
   age: 12,
   friends: ["Shelby", "Court", "Van"]
};
let anotherPerson1 = Object.create(person);
anotherPerson1.name = 'lll'
anotherPerson1.friends.push('zxc')
console.log(anotherPerson1);   // { name: 'lll' }
console.log(person.friends); //[ 'Shelby', 'Court', 'Van', 'zxc' ]

// ? Object.create() 的第二个参数与 Object.defineProperties() 的第二个参数一样:每个新增属性都通过各自的描述符来描述

let anotherPerson = Object.create(person, {
    name: {
        value: "Greg"
    }
});
console.log(anotherPerson.name); // "Greg"

寄生式继承

寄生式继承背后的思路类似于寄生构造函数和工厂模式:创建一个实现继承的函数,以某种方式增强对象,然后返回这个对象。

function createAnother(original) {
    //通过调用函数创建一个对象
    let clone = Object(original)
    clone.sayHi = function () { // 以某种方式增强这个对象
        console.log("hi");
    };
    clone.sayName = function () { // 以某种方式增强这个对象
        console.log(this.name);
    };
    return clone; // 返回这个对象
}
let person = {
    name: "Nicholas",
    friends: ["Shelby", "Court", "Van"]
};
let anotherPerson = createAnother(person);
anotherPerson.sayHi(); // "hi"
anotherPerson.sayName(); // "hi"

寄生时组合继承

function SuperType(name) {
    this.name = name
    this.colors = ['red', 'blue']
}
SuperType.prototype.sayName = function () {
    console.log(this.name)
}
function SubType(name, age) {
    SuperType.call(this, name); // 第二次调用 SuperType()
    this.age = age;
}
SubType.prototype = new SuperType(); // 第一次调用 SuperType()
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge = function () {
    console.log(this.age);
};
let instance1 = new SubType('zxc', 12)
console.log(instance1);

在上面的代码执行后, SubType.prototype上会有两个属性: name 和 colors 。它们都是 SuperType 的实例属性,但现在成为了 SubType 的原型属性。在调用 SubType构造函数时,也会调用SuperType 构造函数,这一次会在新对象上创建实例属性 name 和 colors 。这两个实例属性会遮蔽原型上同名的属性。

如图所示,有两组 name 和 colors 属性:一组在实例上,另一组在 SubType 的原型上。这是 调用两次 SuperType 构造函数的结果,好在有办法解决这个问题。

寄生式组合继承通过盗用构造函数继承属性,但使用混合式原型链继承方法。基本思路是不通过调 用父类构造函数给子类原型赋值,而是取得父类原型的一个副本。说到底就是使用寄生式继承来继承父 类原型,然后将返回的新对象赋值给子类原型。寄生式组合继承的基本模式如下所示:

function inheritPrototype(subType, superType) {
    let prototype = object(superType.prototype); // 创建对象
    prototype.constructor = subType; // 增强对象
    subType.prototype = prototype; // 赋值对象
}

这个 inheritPrototype() 函数实现了寄生式组合继承的核心逻辑。这个函数接收两个参数:子 类构造函数和父类构造函数。在这个函数内部,第一步是创建父类原型的一个副本。然后,给返回的 prototype 对象设置 constructor 属性,解决由于重写原型导致默认 constructor 丢失的问题。最 后将新创建的对象赋值给子类型的原型。如下例所示,调用 inheritPrototype() 就可以实现前面例 子中的子类型原型赋值:

function SuperType(name) {
    this.name = name;
    this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function () {
    console.log(this.name);
}
function SubType(name, age) {
    SuperType.call(this, name)
    this.age = age;
}
inheritPrototype(SubType, SuperType);
SubType.prototype.sayAge = function () {
    console.log(this.age);
};

这里只调用了一次 SuperType 构造函数,避免了 SubType.prototype 上不必要也用不到的属性, 因此可以说这个例子的效率更高。而且,原型链仍然保持不变,因此 instanceof 操作符和 isPrototypeOf() 方法正常有效。寄生式组合继承可以算是引用类型继承的最佳模式。

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

时间: 2022-05-07

JavaScript 常见的继承方式汇总

原型链机制: 在ECMAscript中描述了原型链的概念,并将原型链作为实现继承的主要方法,其基本思想就是利用原型让一个引用类型继承另一个引用类型的属性和方法. 构造函数和原型还有实例之间的关系: 每个构造函数都有一个原型对象(prototype),原型对象都包含一个指向构造函数的指针(constructor),而实例都包含一个指向原型对象的内部指针 ( __propto__ ) .关系图如下图所示: 每一个Function都是Object基类的一个实例,所以每一个Function上都有一个__

一文详解如何用原型链的方式实现JS继承

目录 原型链是什么 通过构造函数创建实例对象 用原型链的方式实现继承 方法1:Object.create 方法2:直接修改 [[prototype]] 方法3:使用父类的实例 总结 今天讲一道经典的原型链面试题. 原型链是什么 JavaScript 中,每当创建一个对象,都会给这个对象提供一个内置对象 [[Prototype]] .这个对象就是原型对象,[[Prototype]] 的层层嵌套就形成了原型链. 当我们访问一个对象的属性时,如果自身没有,就会通过原型链向上追溯,找到第一个存在该属性原

深入讲解JavaScript之继承的多种方式和优缺点

目录 1.原型链继承 2.借用构造函数(经典继承) 3.组合继承 4.原型式继承 5. 寄生式继承 6. 寄生组合式继承 1.原型链继承 function Parent () { this.name = 'kevin'; } Parent.prototype.getName = function () { console.log(this.name); } function Child () { } Child.prototype = new Parent(); var child1 = new

js继承的6种方式详解

原型链继承 原型链继承是ECMAScript的主要继承方式.其基本思想就是通过原型继承多个引用类型的属性和方法.什么是原型链?每个构造函数都会有一个原型对象,调用构造函数创建的实例会有一个指针__proto__指向原型对象,这个原型可能是另一个类型的实例,所以内部可能也有一个指针指向另一个原型,然后就这样形成了一条原型链. 代码: function SuperType() { this.property = true; } SuperType.prototype.getSuperValue =

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

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

原生Javascript实现继承方式及其优缺点详解

目录 前言 原型继承 优点 构造函数继承 优点 缺点 组合式继承 寄生式组合继承 总结 前言 最近在复习javascript的一些基础知识,为开启新的征程做准备.所以开始记录一些自己学习的内容. 那今天的主题是 js的原生继承方式 废话少说,上代码! 首先是我们的父类代码. 在这里我们创建一个Person的类作为父类,它的构造函数需要2个参数name和age. 然后我们在它的原型上添加一个sayHi的方法. //父类 function Person (name, age) { this.name

JS中的六种继承方式以及优缺点总结

目录 前言 原型链继承 构造函数继承 组合继承(原型链继承和构造函数继承组合) 寄生式继承 组合寄生式继承 extends继承 总结 前言 继承是JS世界中必不可少的一个环节,号称JS的三座大山之一,使用这种方式我们可以更好地复用以前的开发代码,缩短开发的周期.提升开发效率 在ES6之前,JS中的类都是通过构造函数模拟的,并不存在真正意义上的类,虽然ES6的类知识一个语法糖

JavaScript 实现继承的几种方式

非ES6代码实现继承的主流方式主要可以分为: 构造继承.原型链继承.构造继承+原型链继承组合继承.以及在组合继承上衍生出的继承方式. 构造继承 (借助call实现) 实现 function Super(age){ this.age = age; this.say = function(){ console.log(this.age) } } function Child(name,age){ Super.call(this,age) this.name = name; } var child =

详述JavaScript实现继承的几种方式(推荐)

ECMAScript只支持实现继承,而且其实现继承主要是依靠原型链来实现的. 原型链 原型链的基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法.每一个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的指针.如果:我们让原型对象A等于另一个类型B的实例,那么原型对象A就会有一个指针指向B的原型对象,相应的B的原型对象中保存着指向其构造函数的指针.假如B的原型对象又是另一个类型的实例,那么上述的关系依旧成立,如此层层递进,就构成了实例与原型的

实现JavaScript中继承的三种方式

一.原型链继承 在原型链继承方面,JavaScript与java.c#等语言类似,仅允许单父类继承.prototype继承的基本方式如下: 复制代码 代码如下: function Parent(){} function Child(){} Child.prototype = new Parent(); 通过对象Child的prototype属性指向父对象Parent的实例,使Child对象实例能通过原型链访问到父对象构造所定义的属性.方法等. 构造通过原型链链接了父级对象,是否就意味着完成了对象

js实现继承的5种方式

本文实例讲述了js实现继承的5种方式.分享给大家供大家参考,具体如下: 1.继承第一种方式:对象冒充 function Parent(username){ this.username = username; this.hello = function(){ alert(this.username); } } function Child(username,password){ //通过以下3行实现将Parent的属性和方法追加到Child中,从而实现继承 //第一步:this.method是作为

JS继承与闭包及JS实现继承的三种方式

前  言 在之前的两篇博客中,我们详细探讨了JavaScript OOP中的各种知识点(JS OOP基础与JS 中This指向详解 . 成员属性.静态属性.原型属性与JS原型链).今天我们来继续探讨剩余的内容吧. 我们都知道,面向对象的三大特征--封装.继承.多态. 封装无非就是属性和方法的私有化,所以我们JS中提供了私有属性和私有方法. 而JS中并没有多态,因此我们说JS是一门基于对象的语言,而非面向对象的语言. 那么,面向对象三大特征中,在JS中最重要的就是继承了. 一.继承的基本概念 使用

JS实现面向对象继承的5种方式分析

本文实例讲述了JS实现面向对象继承的5种方式.分享给大家供大家参考,具体如下: js是门灵活的语言,实现一种功能往往有多种做法,ECMAScript没有明确的继承机制,而是通过模仿实现的,根据js语言的本身的特性,js实现继承有以下通用的几种方式 1. 使用对象冒充实现继承(该种实现方式可以实现多继承) 实现原理:让父类的构造函数成为子类的方法,然后调用该子类的方法,通过this关键字给所有的属性和方法赋值 function Parent(firstname) { this.fname=firs

javascript函数命名的三种方式及区别介绍

javascript函数命名的三种方式及区别介绍 第一 复制代码 代码如下: function fn(val1,val2) { alert(val1+val2); } fn(1,2); 第二 复制代码 代码如下: var fn=function() { alert(val1+val2); } fn(1,2); 第三 复制代码 代码如下: var fn=new Function("alert(val1+val2)"); fn(1,2); 上面三种方式逻辑上是等价的,但是还是有点小区别:区

Python tkinter模块中类继承的三种方式分析

本文实例讲述了Python tkinter模块中类继承的三种方式.分享给大家供大家参考,具体如下: tkinter class继承有三种方式. 提醒注意这几种继承的运行方式 一.继承 object 1.铺tk.Frame给parent: 说明: self.rootframe = tk.Frame(parent) tk.Label(self.rootframe) import tkinter as tk class MyApp(object): def __init__(self, parent)

javascript数组输出的两种方式

本文实例讲述了javascript数组输出的两种方式.分享给大家供大家参考.具体如下: 遍历javascript数组,两种方式: 第一种: 复制代码 代码如下: <script language="javascript" type="text/javascript"> var str = "how are you today"; var arr = str.split(" "); for(var key in ar

JavaScript中实现继承的三种方式和实例

javascript虽然是一门面向对象的语言,但是它的继承机制从一开始设计的时候就不同于传统的其他面向对象语言,是基于原型的继承机制,但是在这种机制下,继承依然有一些不同的实现方式. 方法一:类式继承 所谓的类式继承就是指模仿传统面向对象语言的继承方式,继承与被继承的双方都是"类",代码如下: 首先定义一个父类(或超类): function Person(name){ this.name=name; } Person.prototype.getName=function(){ retu

详细分析Javascript中创建对象的四种方式

前言 使用Javascript创建对象的方式有很多,现在就来列举一下其中的四种方式,并且罗列出了每种方式的优缺点,可以让大家进行选择使用,下面来看看. 工厂模式 function createPerson(name, age){ var obj = new Object(); obj.name = name; obj.age = age; return obj; //一定要返回,否则打印undefined:undefined } var person1 = new createPerson('Y