js中实现继承的五种方法

借用构造函数

这种技术的基本思想很简单,就是在子类型构造函数的内部调用超类型的构造函数。另外,函数只不过是在特定环境中执行代码的对象,因此通过使用apply()和call()方法也可以在新创建的对象上执行构造函数。

function Box(name){
 this.name = name
}
Box.prototype.age = 18

function Desk(name){
 Box.call(this, name) // 对象冒充,对象冒充只能继承构造里的信息
}

var desk = new Desk('ccc')
console.log(desk.name)   // --> ccc
console.log(desk.age)    // --> undefined

从中可以看到,继承来的只有实例属性,而原型上的属性是访问不到的。这种模式解决了两个问题,就是可以传参,可以继承,但是没有原型,就没有办法复用。

组合继承

function Box(name){
 this.name = name
}
Box.prototype.run = function (){
 console.log(this.name + '正在运行...')
}

function Desk(name){
 Box.call(this, name) // 对象冒充
}

Desk.prototype = new Box() // 原型链

var desk = new Desk('ccc')
console.log(desk.name)   // --> ccc
desk.run()         // --> ccc正在运行...

这种继承方式的思路是:用使用原型链的方式来实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承。

原型式继承

原型式继承:是借助原型可以基于已有的对象创建新对象,同时还不必因此创建自定义类型。讲到这里必须得提到一个人,道格拉斯·克罗克福德在2006年写的一篇文章《Prototype inheritance in Javascript》(Javascript中的原型式继承)中给出了一个方法:

function object(o) {   //传递一个字面量函数
 function F(){}     //创建一个构造函数
 F.prototype = o;    //把字面量函数赋值给构造函数的原型
 return new F()     //最终返回出实例化的构造函数
}

看如下的例子:

function obj(o) {
 function F (){}
 F.prototype = o;
 return new F()
}

var box = {
 name: 'ccc',
 age: 18,
 family: ['哥哥','姐姐']
}

var box1 = obj(box)
console.log(box1.name)   // --> ccc
box1.family.push('妹妹')
console.log(box1.family)  // --> ["哥哥", "姐姐", "妹妹"]

var box2 = obj(box)
console.log(box2.family)  // --> ["哥哥", "姐姐", "妹妹"]

因为上述的代码的实现逻辑跟原型链继承很类似,所以里面的引用数组,即family属性被共享了。

寄生式继承

function obj(o) {
 function F (){}
 F.prototype = o;
 return new F()
}
function create(o){
 var clone = obj(o)   // 通过调用函数创建一个新对象
 clone.sayName = function(){   // 以某种方式来增强这个对象
  console.log('hi')
 }
 return clone   // 返回这个对象
}

var person = {
 name: 'ccc',
 friends: ['aa','bb']
}

var anotherPerson = create(person)
anotherPerson.sayName()   // --> hi

这个例子中的代码基于person返回一个新对象————anotherPerson。新对象不仅具有person的所有属性和方法,而且还有自己的sayHi()方法。在主要考虑对象而不是自定义类型和构造函数的情况下,寄生式继承也是一种有用的模式。使用寄生式继承来为对象添加函数,会由于不能做到函数复用而降低效率,这一点与构造函数模式类似。

寄生组合式继承

前面说过,组合继承是Javascript最常用的继承模式,不过,它也有自己的不足。组合继承最大的问题就是无论什么情况下,都会调用过两次超类型构造函数:一次是在创建子类型原型的时候,另一次是在子类型构造函数内部。没错,子类型最终会包含超类型对象的全部实例属性,但我们不得不在调用子类型构造函数时重写这些属性,再来看一下下面的例子:

function SuperType(name){
 this.name = name;
 this.colors = ['red','black']
}
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)
}

第一次调用SuperType构造函数时,SubType.prototype会得到两个属性:name和colors。他们都是SuperType的实例属性,只不过现在位于SubType的原型中。当调用SubType构造函数时,又会调用一次SuperType构造函数,这个一次又在新对象上创建了实例属性name和colors。于是,这两个属性就屏蔽了原型中的两个同名属性。即有两组name和colors属性:一组在实例上,一组在原型上。这就是调用两次SuperType构造函数的结果。解决这个问题的方法就是————寄生组合式继承。
所谓寄生组合式继承,即通过借用构造函数来继承属性,通过原型链的混成形式来继承方法。其背后的基本思路是:不必为了制定子类型的原型而调用超类型的构造函数,我们所需要的无非就是超类型原型的一个副本而已。本质上,就是使用寄生式继承来继承超类型的原型,然后再将结果指定给子类型的原型。寄生组合式继承的基本模式如下:

function object(o) {
 function F (){}
 F.prototype = o;
 return new F()
}
function inheritPtototype(subType, superType){
 var prototype = object(superType.prototype) // 创建对象
 prototype.constructor = subType       // 增强对象
 subType.prototype = prototype        // 指定对象
}

function SuperType(name){
 this.name = name
 this.colors = ['red', 'white']
}

SuperType.prototype.sayName = function(){
 console.log(this.name)
}

function SubType(name,age){
 SuperType.call(this,name)
 this.age = age
}

inheritPtototype(SubType, SuperType)

SubType.prototype.sayAge = function(){
 console.log(this.age)
}

var instance = new SubType('ccc', 18)

instance.sayName()   // --> ccc
instance.sayAge()    // --> 18
console.log(instance)

控制台打印出的结构:

详细的图解:

这个例子的高效率提现在它值调用了一次SuperType构造函数,并且因此避免了在SubType.prototype上面创建不必要的、多余的属性。与此同时,原型链还能保持不变;因此,还能够正常使用instanceof和isPrototypeOf()。这也是很多大厂用的继承方式。

以上就是js中实现继承的五种方法的详细内容,更多关于js 实现继承的资料请关注我们其它相关文章!

(0)

相关推荐

  • 夯基础之手撕javascript继承详解

    前言 提到JS继承,你首先想到的什么? 面试 继承方式 优缺点...,js继承作为曾经的苦主,我看了忘,忘了看,看了又忘,OMG,都9012年了面试官还不放过我. ok,开开玩笑,接下来言归正传,来聊聊js继承这个经典的话题. JS的"类" javascript不像java,php等传统的OOP语言,js本身并没有类这个概念,那么它是怎么实现类的模拟呢? 构造函数方式 原型方式 混合方式 构造函数方式 Function Foo (name) { this.name = name thi

  • JavaScript类的继承多种实现方法

    类的继承 1 子承父业 extends(继承父类的普通函数)(方法) class Father { constructor() { } money() { console.log(100); } } class Son extends Father { } class sunzi extends Son { } var yxf = new Father; var lbw = new Son; var bb = new sunzi; console.log(yxf.money()); consol

  • Javascript组合继承方法代码实例解析

    组合继承 组合继承,指将原型链和借用构造函数的技术组合到一块,从而发挥二者之长的一种继承模式.其背后思路使用用原型链实现对原型属性和方法的继承,而通过构造函数来实现对实例属性的继承.这样,即通过在原型上定义方法实现了函数复用,又能够保证每个实例都有它自己的属性. 下面来看一个例子 function SuperType(name) { this.name = name; this.color = ['red', 'blue', 'green']; }; // SuperType.prototype

  • JavaScript面向对象三个基本特征实例详解【封装、继承与多态】

    本文实例讲述了JavaScript面向对象三个基本特征.分享给大家供大家参考,具体如下: 了解过面向对象的同学应该都知道,面向对象三个基本特征是:封装.继承.多态,但是对于这三个词具体可能不太了解.对于前端来讲接触最多的可能就是封装与继承,对于多态来说可能就不是那么了解了. 封装 在说封装之先了解一下封装到底是什么? 什么是封装 封装:将对象运行所需的资源封装在程序对象中--基本上,是方法和数据.对象是"公布其接口".其他附加到这些接口上的对象不需要关心对象实现的方法即可使用这个对象.

  • 详解JavaScript之ES5的继承

    自从有了ES6的继承后,ES5的继承也退出了舞台,在实际开发也不会用得着,但在面试或许用的着: 先看看ES6的继承 class Father{ constructor(a){ console.log(a); } play(){ console.log("aaa"); } static run(){ console.log("static"); } } class Son extends Father{ constructor(){ super(); } } var

  • JS继承实现方法及优缺点详解

    前言 JS作为面向对象的弱类型语言,继承也是其非常强大的特性之一.那么如何在JS中实现继承呢?让我们拭目以待. JS继承的实现方式 既然要实现继承,那么首先我们得有一个父类,代码如下: // 定义一个动物类 function Animal (name) { // 属性 this.name = name || 'Animal'; // 实例方法 this.sleep = function(){ console.log(this.name + '正在睡觉!'); } } // 原型方法 Animal

  • 6种JavaScript继承方式及优缺点(小结)

    温馨提示:想要更好的理解JS继承方式,须了解构造函数.原型对象.实例化对象.原型链等概念 第一种:原型链继承 利用原型链的特点进行继承 function Parent(){ this.name = 'web前端'; this.type = ['JS','HTML','CSS']; } Parent.prototype.Say=function(){ console.log(this.name); } function Son(){}; Son.prototype = new Parent();

  • JavaScript使用prototype属性实现继承操作示例

    本文实例讲述了JavaScript使用prototype属性实现继承操作.分享给大家供大家参考,具体如下: JS并没有显式的继承语法,在JS中所有的对象都是Object的子类实现, 因而对象之间是平等关系. 尽管如此我们可以通过特殊的方法达到继承的效果. 当然JS也不能直接定义类, 我们通过定义函数可以得到一个同名的类 , 同时这个函数就是这个类的构造器, 在定义函数时以this修饰的变量就是定义的 类的实例中的属性,当这个属性时函数时,  就可以认为这个属性变成了一个实例方法 //定义一个Pe

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

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

  • JavaScript中的几种继承方法示例

    1.原型链继承 原理: 子类原型指向父类实例对象实现原型共享,即Son.prototype = new Father(). 这里先简单介绍下原型 js中每个对象都有一个__proto__属性,这个属性指向的就是该对象的原型.js中每个函数都有一个prototype属性,这个属性指向该函数作为构造函数调用时创建的实例的原型.原型对象上有一个constructor属性,指向创建该对象的构造函数,该属性不可枚举. var obj = {}; obj.__proto__ === Object.proto

  • JS中的继承操作实例总结

    本文实例讲述了JS中的继承操作.分享给大家供大家参考,具体如下: 1.原型链继承 function SuperType() { this.property = true; } SuperType.prototype.getSuperValue = function() { return this.property; } function SubType() { ths.subproperty = false; } SubType.prototype = new SuperType(); // 实

  • JavaScript原型继承和原型链原理详解

    这篇文章主要介绍了JavaScript原型继承和原型链原理详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 在讨论原型继承之前,先回顾一下关于创建自定义类型的方式,这里推荐将构造函数和原型模式组合使用,通过构造函数来定义实例自己的属性,再通过原型来定义公共的方法和属性. 这样一来,每个实例都有自己的实例属性副本,又能共享同一个方法,这样的好处就是可以极大的节省内存空间.同时还可以向构造函数传递参数,十分的方便. 这里还要再讲一下两种特色的构造

随机推荐