第4集丨JavaScript 使用原型(prototype)实现继承——最佳实战2

article/2025/8/30 11:45:55

目录

  • 一、临时构造器方式
    • 1.1 代码实现
    • 1.2 代码分析
  • 二. 增加uber属性,用于子对象访问父对象
    • 2.1 实现分析
    • 2.2 代码实现
  • 三. 将继承封装成extend()函数
    • 3.1 代码实现
      • 3.1.1 临时构造器实现extend()
      • 3.1.2 原型复制实现extend2()
    • 3.2 代码测试
      • 3.2.1 测试extend()函数
      • 3.2.1 测试extend2() 函数
  • 四、实例对象原型继承:以实例对象为原型创建新对象
    • 4.1 功能分析
    • 4.2 功能测试

书接上集,在上集中我们给出了一个需求说明,要求利用现学的知识实现原型(prototype)继承,并且我们给出了三种实现方式。但是这三种方式各自有优缺点,都不能很好的满足要求,那是否还有其他更好的实现方式呢?

在看本文之前,诸位可以自己思考下?带着问题去学习总是能够学到更多内容。在以后的岁月中,会找一些有意义的句子,与诸君共勉。

在时间的稿纸上,每个人都在写着自己的历史。当你抓着今天时,你就会前进一步,当你丢弃今天时,你就会停滞不动。

一、临时构造器方式

上集中,我们利用prototype方式实现继承,会带来一个问题:所有的属性都指向了一个相同的对象,父对象就会受到子对象属性的影响。

如何解决这个问题?

这时就必须利用某种中介来打破这种连锁关系。我们可以用一个临时的构造器函数来充当中介。即:我们创建一个空函数f(),并将其原型设置为父级构造器,然后我们既可以用new F()来创建一些不包含父对象属性的对象,同时又可以从父对象prototype中继承一切了。

1.1 代码实现

function Shape(){}
Shape.prototype.name = "Shape";
Shape.prototype.toString = function () {return this.name;
}
function TwoDShape(){}
var F = function(){};
F.prototype = Shape.prototype;
TwoDShape.prototype = new F();
TwoDShape.prototype.constructor = TwoDShape;
TwoDShape.prototype.name = "2D Shape";
function Triangle(side, height){this.side = side;this.height = height;
}
var F = function(){};
F.prototype = TwoDShape.prototype;
Triangle.prototype = new F();
Triangle.prototype.constructor = Triangle;
Triangle.prototype.name = "Triangle";
Triangle.prototype.getArea = function () {return this.side * this.height / 2;
}
var my = new Triangle(5, 10);
console.log(my.getArea());  //25
console.log(my.name);   //Triangle
console.log(my.toString()); //Triangle
var supers = new Shape();
console.log(supers.name);   //Shape 注意

1.2 代码分析

从下图中我们可以看出my 对象的结构

在这里插入图片描述
那么这个时候toString() 方法查找分几步呢?

  • 在本类中查找
  • my对象的my.[[Prototype]] 中找,此时该[[Prototype]] 对象为TwoDShape的实例,里面只有一个name = "Triangele"属性。
  • my对象的my.[[Prototype]].[[Prototype]] 中找,为Shape的实例,同样里面也只有一个name="2D Shape" 属性
  • my.[[Prototype]].[[Prototype]].[[Prototype]] 中找,为Object实例,此时,就已经找到了 toString() 方法。

二. 增加uber属性,用于子对象访问父对象

2.1 实现分析

uber 属性的名字原本应该是“superclass”,但是这样一来显得JavaScript中有了类的概念(在ES6中引入了class),或许该叫super,但是superJavaScript的保留字,因为改成uber

  • 我们给构造函数增加一个uber属性,使其值为父类的原型Triangle.uber = TwoDShape.prototype;
  • 如下图所示,此时构造函数Triangle() 中,包含了uber属性
    在这里插入图片描述

2.2 代码实现

function Shape(){}
Shape.prototype.name = "Shape";
Shape.prototype.toString = function () {var result = [];if(this.constructor.uber){result[result.length] = this.constructor.uber.toString();}result[result.length] = this.name;return result.join(', ');
}
function TwoDShape(){}
var F = function(){};
F.prototype = Shape.prototype;
TwoDShape.prototype = new F();
TwoDShape.prototype.constructor = TwoDShape;
TwoDShape.uber = Shape.prototype;   //增加uber属性,指向父类的原型
TwoDShape.prototype.name = "2D Shape";
function Triangle(side, height){this.side = side;this.height = height;
}
var F = function(){};
F.prototype = TwoDShape.prototype;
Triangle.prototype = new F();
Triangle.prototype.constructor = Triangle;
Triangle.uber = TwoDShape.prototype;
Triangle.prototype.name = "Triangle";
Triangle.prototype.getArea = function () {return this.side * this.height / 2;
}
var my = new Triangle(5, 10);
console.log(my.getArea());  //25
console.log(my.name);   //Triangle
console.log(my.toString()); //Shape, 2D Shape, Triangle
var supers = new Shape();
console.log(supers.name);   //Shape

三. 将继承封装成extend()函数

  • 为了代码的封装和重用,我们可以将实现继承的部分抽取出来,定义一个extend()函数。

3.1 代码实现

extend()相比,extend2() 显得略逊一筹。因为这里执行的是子对象原型的逐一拷贝,而非简单的原型链查询。所以我们必须要记住,这种方式仅适用于只包含基本数据类型的对象,所有的对象类型(包括函数和数组)都是不可复制的,因为他们只支持引用传递。

这里toString()方法实际上是同一个函数对象。这里只是一个函数引用,函数本身并没有再次被创建。

优缺点

  • extend2()方法的效率要低于extend()方法,主要是前者对部分原型属性进行了重建。
  • 当然了,这对于只包含基本数据类型的对象来说,是有好处的。因为这样做能使属性查找操作更多的停留在对象本身,从而减少了原型链上的查找。

3.1.1 临时构造器实现extend()

//定义extend函数
function extend(Child, Parent){var F = function () {};F.prototype = Parent.prototype;Child.prototype = new F();Child.prototype.constructor = Child;Child.uber = Parent.prototype;}

3.1.2 原型复制实现extend2()

function extend2(Child, Parent){var p = Parent.prototype;var c = Child.prototype;for (var i in p){c[i] = p[i];}c.uber = p; //扩展子类原型,增加一个uber属性
}

3.2 代码测试

3.2.1 测试extend()函数

  • 利用上述extend() 函数实现继承。

//定义Shape类
function Shape(){}
Shape.prototype.name = "Shape";
Shape.prototype.toString = function () {return this.name;
}//定义TwoDShape类
function TwoDShape(){}
extend(TwoDShape, Shape);
TwoDShape.prototype.name = "2D Shape";//定义Triangle类
function Triangle(side, height){this.side = side;this.height = height;
}
extend(Triangle, TwoDShape);
Triangle.prototype.name = "Triangle";
Triangle.prototype.getArea = function () {return this.side * this.height / 2;
}//测试
var my = new Triangle(5, 10);
console.log(my.getArea());  //25
console.log(my.name);   //Triangle
console.log(my.toString()); // Triangle
var supers = new Shape();
console.log(supers.name);   //Shape

3.2.1 测试extend2() 函数

//定义Shape类
function Shape(){}
Shape.prototype.name = "Shape";
Shape.prototype.toString = function () {return this.name;
}//定义TwoDShape类
function TwoDShape(){}
extend(TwoDShape, Shape);//extend测试结果
var td = new TwoDShape();console.log(td.name);   //Shapeconsole.log(TwoDShape.prototype.name); //Shapeconsole.log(td.__proto__.name); //Shapeconsole.log(td.hasOwnProperty("name")); //falseconsole.log(td.__proto__.hasOwnProperty("name"));   //false//extend2测试结果
var td = new TwoDShape();
console.log(td.__proto__.hasOwnProperty("name"));   //true
console.log(td.__proto__.hasOwnProperty("toString"));   //true
console.log(td.__proto__.toString == Shape.prototype.toString); //true

四、实例对象原型继承:以实例对象为原型创建新对象

4.1 功能分析

基于这种在对象之间直接构建继承关系的理念,我们可以创建一个object()函数,接受父对象,并返回一个以该对象为原型的新对象。这和Object.create() 类似。如下代码所示:

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

如果需要访问父类,则可加添加uber属性。

function object(o){var n;var F = function(){};F.prototype = o;n = new F();n.uber = o;return n;
}

4.2 功能测试

//定义Shape类function Shape(){}Shape.prototype.name = "Shape";Shape.prototype.toString2 = function () {return this.name;}var shapeObj = new Shape();var twoDShape = object(shapeObj);twoDShape.name = "HHH"console.log(twoDShape.toString2()); //HHH
  • twoDShape 对象的结构如下所示
    在这里插入图片描述

http://chatgpt.dhexx.cn/article/lh2YNFtF.shtml

相关文章

jstorm安装配置

jstorm安装配置 前言下载配置启动 前言 jstorm介绍 jstorm JStorm 是一个类似Hadoop MapReduce的系统, 用户按照指定的接口实现一个任务,然后将这个任务递交给JStorm系统,Jstorm将这个任务跑起来,并且按7 * 24小时运行起来&…

《JavaScript》重学JS-细聊一下prototype、__proto__与constructor(超详解版)

求关注,求收藏,求点赞,非常感谢!你的每一个阅读都是我的力量源泉! 前言 最近在比对class以及将class编译成es5后的代码,看的是晕头转向,尤其在原型链这一块,发现之前的了解实在有些…

【React进阶之路01】- JSX演变成真实DOM

什么是 JSX JSX 是 ECMAScript 一个类似 XML 的语法扩展。基本上,它只是为 React.createElement() 函数提供语法糖,从而让在我们在 JavaScript 中,使用类 HTML 模板的语法,进行页面描述。 JSX编译(babel)…

JStorm—实时流式计算框架入门介绍

JStorm介绍 JStorm是参考storm基于Java语言重写的实时流式计算系统框架,做了很多改进。如解决了之前的Storm nimbus节点的单点问题。   JStorm类似于Hadoop MapReduce系统,用户按照指定的接口去实现一个任务,任务提交给JStorm进行运行&…

jstorm基本概念

基本概念 longdafeng edited this page on 29 Sep 4 revisions Pages 69 0.7.1 changelist0.9.0 change list0.9.0 性能测试0.9.1_change_list0.9.2_change_list0.9.3_change_listAck 机制Acking Framework ImplementationApplication examplesBasic conceptionBuild JStormD…

ReactJS入门之Model层

目录 一:分层 二:使用DVA进行数据分层管理 三:在model中请求数据 四:mock数据 一:分层 上图中,左侧是服务端代码的层次结构,由 Controller 、 Service 、 Data Access 三层组成服务端系统…

初识Jstorm 多个bolt应用

最近接到任务说要使用jstorm处理业务,之前没接触过,只能硬着头皮来,接下来谈谈我这两天的收获 1,怎么了解jstorm,这个答案没什么固定的,但是我个人比较喜欢去看官方的文档,如果官方的文档实在找…

大数据(十五) - JStorm

JStorm 是一个分布式实时计算引擎,是淘宝开源的 随着Storm 的规模越来越大,发现原有的很多Storm设计,只能适合小集群中运行,当集群规模超过100台时,均会出现一些或这或那的问题。JStorm 比Storm更稳定,更强…

初识JavaScript---(1)

初识JavaScript———(1)!!! 一、初识JavaScript 1.什么是JavaScript? JavaScript是运行在浏览器上的脚本语言,简称JS。JavaScript程序不需要我们程序员手动编译,编写完源代码之后…

【JavaScript高级进阶】构造函数和原型,学会prototype

目录 前言 1.构造函数和原型 1.1使用prototype解决内存浪费的问题 1.2constructor构造函数构造器构造函数 2.原型链 2.1js中成员查找规则 2.2原型对象this指向 2.3扩展内置对象 3.call作用 4.继承 4.1利用原型对象继承 写在最后 前言 哈喽哈喽大家好,因为…

Jstorm 基本概念

本质 基于消息的流水线处理模型是一套类似MapReduce一样的编程模型内核是一套调度系统 适合的业务 高并发的计算任务数据流之间相互无依赖 编程模型 Topology:即一个数据流的拓扑结构,包含多个Spout和BoltSpout:从外部获取数据&#xff…

JStorm和Storm比较

1、What——JStorm是什么?  概述: JStorm 是一个分布式实时计算引擎,类似Hadoop MapReduce的系统, 用户按照规定的编程规范实现一个任务,然后将这个任务递交给JStorm系统,Jstorm将这个任务跑起来&#xf…

JStorm Storm 上手demo

折线之间的内容整理自: http://blog.csdn.net/suifeng3051/article/details/38369689 -------------------------------------------------------------------------------------------------------------------------------------------- 在全面介绍Storm之前&…

PyTorch从零开始实现Transformer

文章目录 自注意力Transformer块编码器解码器块解码器整个Transformer参考来源全部代码(可直接运行) 自注意力 计算公式 代码实现 class SelfAttention(nn.Module):def __init__(self, embed_size, heads):super(SelfAttention, self).__init__()self.e…

jstorm storm 入门demo

jstorm和storm比较 jstorm 是阿里巴巴开源的基于storm采用Java重写的一套分布式实时流计算框架,使用简单,特点如下: 1,开发非常迅速: 接口简单,容易上手,只要遵守Topology,Spout,Bo…

JStorm介绍

一、简介 JStorm是一个分布式实时计算引擎。JStorm是一个类似于Hadoop MapReduce的系统,用户按照指定的接口实现一个任务,然后将这个任务交给JStorm系统,JStorm将这个任务跑起来,并按7*24小时运行。如果中间一个worker发生了意外…

马氏距离实例详解

介绍 马氏距离是由印度统计学家马哈拉诺比斯(P. C. Mahalanobis)提出的,表示数据的协方差距离。它是一种有效的计算两个未知样本集的相似度的方法。与欧氏距离不同的是它考虑到各种特性之间的联系(例如:一条关于身高的…

距离度量:闵氏、欧式、马氏、余弦、汉明等

目录 1. 闵氏距离(Minkowski Distance) 2. 欧式距离(Euclidean Distance) 3. 标准化欧式距离(Standardized Euclidean distance) 4. 马氏距离(Mahalanobis Distance) 5. 余弦距…

马氏距离(Mahalanobis Distance)推导及几何意义

看了一些博客对马氏距离的解释,似乎没有讲到本质的地方,本文从欧氏距离存在的问题开始入手,一步步推导出马氏距离,并得出结论:原始空间中的马氏距离等于坐标旋转变换及缩放后的空间中的欧氏距离。 假设数据集 X ∈ R N…

直观理解--马氏距离

首先我们很了解欧氏距离了,就是用来计算欧式空间(就是我们常见的坐标系)中两个点的距离的。 比如点 x ( x 1 , … , x n ) x (x_1,…,x_n) x(x1​,…,xn​) 和 y ( y 1 , … , y n ) y (y_1,…,y_n) y(y1​,…,yn​) 的欧氏距离为&…