博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
深入理解javascript中的继承机制(1)
阅读量:6878 次
发布时间:2019-06-26

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

javascript中的继承机制是建立在原型的基础上的,所以必须先对原型有深刻的理解,笔者在之前已经写过关于js原型的文章。

我们都知道,每个函数function都会有一个属性,这个属性就是原型prototype,它是一个引用,它指向一个对象object。当我们使用new操作符调用构造函数,创建一个新对象的时候,这个新对象就会拥有一个指向它的构造函数的原型对象的神秘链接,在浏览器中一般是__proto__,通常我们也称为它的原型对象。这就可以理解为,new出来的对象继承拥有了了它的构造函数的原型对象,这就隐约有一点继承的概念了。

#原型链继承机制

原型链的概念就是多个这样的对象通过proto相互关系起来

上图可以看到,对象A有一系列的属性,但其中的部分属性是藏在原型之中的,而这个原型又指向了对象B,所以对象A间接也拥有了对象B的部分属性,同理,对象B的部分属性是藏在proto中,而proto又指向了对象C. 这样一环一环的往上递加,显然最后会指向Object.prototype,它是所有对象的父对象。

下面我们就通过一个实例来说明,原型链继承机制的实现与原理 我们有三个构造函数,Shape,2DShape, Triangle。 很显然,它们之间的继承关系,应该是Shape,2DShape,Triangle. 下面分别定义三个构造函数:

function Shape(){    this.name = 'Shape';    this.toString = function () {        return this.name;    };}function TwoDShape(){    this.name = '2D shape';}function Triangle(side, height){    this.name = 'Triangle';    this.side = side;    this.height = height;    this.getArea = function () {        return this.side * this.height / 2;    };}复制代码

接下来实现它们之间的原型链继承关系:

TwoDShape.prototype = new Shape();Triangle.prototype = new TwoDShape();复制代码

当我们覆盖原型对象的时候,不要忘了原型的陷阱(读者若不清楚,可以参考笔者介绍原型的博文)

TwoDShape.prototype.constructor = TwoDShape;Triangle.prototype.constructor = Triangle;复制代码

这样我们就实现了原型链的继承关系。 每一个new出来的TwoDShape对象的proto属性都指向一个Shape对象,所以它可以拥有Shape对象的属性和方法,同理,每一个new出来的Triangle对象的proto属性都指向一个TwoDShape对象,而TwoDShape又继承至Shape,所以这样就形成了一个原型链。

下面我们对以上原型链关系进行测试

上图我们可以看到清晰的一个原型链关系。

我们看到实现原型链继承关系之后,my作为子对象,同时都是父对象的一种,这是符合java等语言中继承的概念的。

将共有的属性放进原型中

如上个例子中的,name属性是三中对象共有的,上个例子每个单独的对象都会new出一个name属性,这样就造成了对空间的浪费。 所以我们将name属性移到原型中去

function Shape() {}Shape.prototype.name = 'Shape';复制代码

就不用每次都new出一个name属性,而是共用原型属性里面的name属性。

下面我们就用这种思想完善之前的例子

// constructorfunction Shape() {}// augment prototypeShape.prototype.name = 'Shape';Shape.prototype.toString = function () {    return this.name;};// another constructorfunction TwoDShape() {}// take care of inheritanceTwoDShape.prototype = new Shape();TwoDShape.prototype.constructor = TwoDShape;// augment prototypeTwoDShape.prototype.name = '2D shape';function Triangle(side, height) {    this.side = side;    this.height = height;}// take care of inheritanceTriangle.prototype = new TwoDShape();Triangle.prototype.constructor = Triangle;// augment prototypeTriangle.prototype.name = 'Triangle';Triangle.prototype.getArea = function () {    return this.side * this.height / 2;};复制代码

将部分共享属性移到原型里去之后,原型链的继承关系如图,对比之前简洁了一些,因为没有多余的重复属性

这里调用toString方法得到相同的结果,但与之前略有不同,这里要多搜索一次,因为toString方法是属于Shape的原型属性里的。于是效率就有所降低。

同时,这种模式还有一个缺陷看下面的例子

我们访问Shape对象的name属性结果显示的确实Triangle,这是为什么呢? 其实很简单,因为我们所有的原型都指向同一个对象,而每个对象的原型属性只是取得了指向唯一的原型对象的指针,所以只要改变了它,所有的都会改变了 因为这句: Triangle.prototype.name = 'Triangle'; 所以会导致Shape,TwoDShape的name属性都都为Triangle。

所以在某些时候,就没法使用这种继承模式,这种将共享的属性移到原型中的模式,会产生子对象覆盖掉父对象共有属性的缺陷。

转载地址:http://cbgfl.baihongyu.com/

你可能感兴趣的文章
学习JDK1.8集合源码之--ArrayDeque
查看>>
但那不是爱
查看>>
C++、Java、JavaScript中的正则表达式
查看>>
Codeforces 935 C Fifa and Fafa
查看>>
浏览器根对象document之方法概述
查看>>
纯小白入手 vue3.0 CLI - 3.1 - 路由 ( router )
查看>>
用正交多项式作最小二乘拟合的java实现(转)
查看>>
ActiveMQ相关背景(转)
查看>>
深入理解Linux修改hostname(转)
查看>>
对TCP/IP网络协议的深入浅出归纳(转)
查看>>
Redis危险命令重命名、禁用
查看>>
归并排序(Merge Sort)
查看>>
es6 语法
查看>>
vue学习(八) vue中样式 class 定义引用
查看>>
基数排序
查看>>
MVC数据库数据分页显示
查看>>
ASP.NET MVC中Unobtrusive Ajax的妙用
查看>>
leetcode------Set Matrix Zeroes
查看>>
Maven 入门篇 ( 上 )
查看>>
在SQL语句中使用参数
查看>>