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

目录

  • 一、原型继承与属性拷贝
    • 1.1 功能说明
    • 1.2 功能测试
  • 二、多重继承
    • 2.1 功能实现
    • 2.2 功能测试
  • 三、寄生式继承
  • 四、构造器借用
    • 4.1 简单实现
    • 4.2 进化版
      • 4.2.1 功能实现
      • 4.2.2 案例测试
  • 五、借用构造器和原型复制
  • 六 综合案例
    • 6.1 需求说明
    • 6.2 代码实现

一、原型继承与属性拷贝

1.1 功能说明

对于继承应用来说,主要目标是将一些现有的功能归为己有。也就是说,我们在新建一个对象时,通常首先应该继承于现有对象,然后在为其添加额外的方法和属性。对此,我们可以通过一个函数调用来完成,并且在其中混合使用我们刚才所讨论的两种方式:

  • 使用原型继承的方式克隆现存对象
  • 其他对象使用属性拷贝的方式
function objectPlus(o, stuff){var n;var F = function(){};F.prototype = o;n = new F();n.uber = o;for(var i in stuff){n[i] = stuff[i];}return n;
}

这个函数用于接受两个参数,o 用于继承,而另一个对象stuff 则用于拷贝方法和属性.

1.2 功能测试

var shape = {name: 'shape',toString: function(){return this.name;}
}
var twoDee = objectPlus(shape,{name: '2D shape',toString: function(){return this.uber.toString() + ", " + this.name; }
})
var triangle = objectPlus(twoDee,{name: 'triangle',getArea: function(){return this.side * this.height / 2 ;},side: 0,height: 0
})var my = objectPlus(triangle,{side: 4,height: 4
})console.log(my.getArea());  //8
console.log(my.toString()); //shape, 2D shape, triangle, triangle。这里有两个triangle,是因为我们在具体实例化时是//继承于triangle对象的,所以多了一层继承关系,我们也可以给实例一个新的名字。例如下面var my = objectPlus(triangle,{side: 4,height: 4,name: 'My Ext'
})
console.log(my.toString()); //shape, 2D shape, triangle, My Ext, 这里就变成了新的名字了

二、多重继承

所谓多重继承,通常是指一个子类对象中不止一个父对象的继承模式。对于JavaScript 这样的动态语言来说,实现多重继承是很简单的,尽管语言本身没有提供特殊的语法单元多重继承实现是极其简单的,我们只需要延续属性拷贝的继承思路依次扩展对象,不对其所继承的对象数量参数进行输入限制即可实现。

2.1 功能实现

下面,我们创建一个multi() 函数,它可以接受任意数量的输入性对象。然后,我们在其中实现了一个双重循环,内层循环用于拷贝属性,而外层循环用于遍历函数参数中所传递进来的所有对象。

function multi(){	//这里是按照参数传递的顺序循环的,而且是浅拷贝var n = {}, stuff, j = 0, len = arguments.length;for(var j = 0; j < len; j++){stuff = arguments[j];for(var i in stuff){n[i] = stuff[i];}}return n;
}

2.2 功能测试

需要注意的是:multi() 函数中循环是按照对象输入的顺序来进行遍历的。如果两个对象拥有相同的属性,以最后一个对象为准。

var shape = {name: 'shape',toString: function(){return this.name;}
}var twoDee = {name: '2D shape',dimensions: 2,numbers: [1,2,3]
}var triangle = multi(shape, twoDee, {name: 'triangle',getArea: function(){return this.side * this.height / 2; },side: 5,height: 10
})console.log(triangle.getArea());    //25
console.log(triangle.dimensions);   //2
console.log(triangle.toString());   //triangletriangle.numbers.push(4);
console.log(triangle.numbers);     //[1,2,3,4]
console.log(twoDee.numbers);        //[1,2,3,4] 显然这个是浅拷贝的属性

三、寄生式继承

基本思想是:我们可以在创建对象的函数中,直接吸收其他对象的功能,然后对其进行扩展并返回。就好像所有的工作都是自己做的一样。

代码示例如下

function object(o){var n;var F = function(){};F.prototype = o;n = new F();n.uber = o;return n;
}
var twoD = {name: "2D shape",dimensions: 2
}function triangle(s, h){var that = object(twoD);that.name = 'triangle';that.getArea = function () {return this.side * this.height / 2;}that.side = s;that.height = h;return that;
}
//由于这里triangle是一个普通函数,不属于构造函数,所以调用它是不需要要new操作符的,但是
//该函数返回的是要给对象,所以在我们错误的使用了new操作符,也会正常工作
var t = triangle(5,6);
console.log(t.dimensions);  //2
console.log(t.getArea());   //15var t2 = new triangle(5,5); //照样正常工作
console.log(t2.getArea());  //12.5

四、构造器借用

子对象构造器可以通过call()apply()方法来调用父对象的构造器。因而,它通常被称为构造器盗用法,或者构造器借用法。

4.1 简单实现

在这里新的Triangle 对象继承了父对象的id 属性,但它并没有继承父对象原型中的其他任何东西。之所以对象中不包含Shape的原型属性,是因为我们从来没有调用new Shape()创建任何一个实例,

function Shape(id){this.id = id;
}
Shape.prototype.name = "Shape";
Shape.prototype.toString = function () {return this.name;}function Triangle(){Shape.apply(this,arguments);
}
Triangle.prototype.name = "triangle";
var t = new Triangle(101);
console.log(t.name);    //triangle
//在这里新的Triangle对象继承了父对象的id属性,但它并没有继承父对象原型中的其他任何东西
//之所以对象中不包含Shape的原型属性,是因为我们从来没有调用new Shape()创建任何一个实例,
//自然其原型也从来没有被用到。所以底下打印的是[object object]
console.log(t.toString());  //[object object],

4.2 进化版

那么如使其包含Shape中的原型呢?我们可以对Triangle构造器进行如下重定义:

4.2.1 功能实现

function Triangle(){Shape.apply(this,arguments);
}
Triangle.prototype = new Shape();
Triangle.prototype.constructor = Triangle;

在这种继承模式中,父对象的属性是以子对象自身的属性的身份来重建的(这与原型链模式中的子对象属性正好相反)。这也体现了构造器借用法的一大优势:当我们创建一个继承于数组或者其他对象类型的子对象时,将获得一个完完全全的新值(不是一个引用),对它做任何修改都不会影响父对象。
但这种模式也是有缺点的,因为这种情况下父对象的构造器往往会被调用两次:一次发生在通过apply方法继承其自身属性时,而另一次则发生在通过 new 操作符继承其原型时。这样一来,父对象的自身属性事实上被继承了两次。

4.2.2 案例测试

下面,我们来做一个简单的演示:

function Shape(id){this.id = id;
}
Shape.prototype.name = "Shape";
Shape.prototype.toString = function () {return this.name;}function Triangle(){Shape.apply(this,arguments);
}
Triangle.prototype = new Shape(101);    //为了演示加上了101,如果不加的话底下打印的就是undefined,虽然是,为什么是undefined,因为你没有传入参数,在执行Shape构造器的时候给id就是没有定义的值,所以this。id就赋值成了undefined,但是原型中还有有这个属性的,还是重复定义了两次
Triangle.prototype.constructor = Triangle;
Triangle.prototype.name = "triangle";var t = new Triangle(202);
console.log(t.id);  //202
console.log(t.__proto__.id);    //101delete t.id;    //删除之后
console.log(t.id);  //101

五、借用构造器和原型复制

对于这种由于构造器的双重调用而带来的重复执行问题,实际上是很容易修正的。我们可以在父类构造器上调用apply()方法,以获得其全部的自身属性,然后在用一个简单的迭代器对其原型属性执行逐项拷贝。

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属性
}function Shape(id){this.id = id;
}
Shape.prototype.name = "Shape";
Shape.prototype.toString = function () {return this.name;}function Triangle(){Shape.apply(this,arguments);
}
extend2(Triangle,Shape);
Triangle.prototype.name = "triangle";var t = new Triangle(202);
console.log(t.toString());  //triangle
console.log(t.id);  //202
console.log(t.__proto__.id);    //undefined

六 综合案例

6.1 需求说明

我们利用所学的知识来写一个综合案例,结合canvas 绘制如下图形:

在这里插入图片描述

6.2 代码实现

<!DOCTYPE html>
<html>
<head lang="en"><meta charset="UTF-8"><title>canvas+prototype</title>
</head>
<body><canvas id="canvas" height="600" width="800"></canvas>
</body>
</html>
	//定义点类
function Point(x, y) {this.x = x;this.y = y;
}//计算两点之间的直线距离
function Line(p1, p2){this.p1 = p1;this.p2 = p2;this.length = Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2));
}//定义父类形状
function Shape(){this.points = [];this.lines = [];this.init();
}
Shape.prototype = {constructor: Shape,init: function(){if(typeof this.context === 'undefined'){var canvas = document.getElementById('canvas');Shape.prototype.context = canvas.getContext("2d");}},draw: function(){var ctx = this.context;ctx.strokeStyle = this.getColor();ctx.beginPath();ctx.moveTo(this.points[0].x, this.points[0].y);for(var i = 1; i<this.points.length; i++){ctx.lineTo(this.points[i].x, this.points[i].y);}ctx.closePath();ctx.stroke();},getColor: function(){var rgb = [];for(var i = 0; i < 3; i++){rgb[i] = Math.round(255 * Math.random());}return 'rgb(' + rgb.join(',') + ')';},getLines: function(){if(this.lines.length > 0){return this.lines;}var lines = [];for(var i = 0; i < this.points.length; i++){lines[i] = new Line(this.points[i],(this.points[i+1]) ? this.points[i+1] : this.points[0]);}this.lines = lines;return lines;},getArea: function () {      //面积},getPerimeter: function(){   //z周长var lines = this.getLines();var perim = 0;for(var i = 0; i < lines.length; i++){perim += lines[i].length;}return perim;}}//定义三角形
function Triangle(a, b, c){this.points = [a, b, c];this.getArea = function () {var p = this.getPerimeter();var s = p / 2;return Math.sqrt(s * (s - this.lines[0].length)* (s - this.lines[1].length)* (s - this.lines[2].length));}
}//定义矩形
function Rectangle(p, side_a, side_b){this.points = [p,new Point(p.x + side_a, p.y),new Point(p.x + side_a, p.y + side_b),new Point(p.x, p.y + side_b)]this.getArea = function () {return side_a * side_b;}
}//定义正方形
function Square(p, side){Rectangle.call(this, p, side, side);
}//修改原型链
var s = new Shape();
Triangle.prototype = s;
Rectangle.prototype = s;
Square.prototype = s;//测试
var p1 = new Point(100, 100);
var p2 = new Point(300, 100);
var p3 = new Point(200, 0);//三角形
var t = new Triangle(p1, p2, p3);
t.draw();
console.log(t.getPerimeter());
console.log(t.getArea());//矩形
var r = new Rectangle(new Point(200, 200), 50, 100);
r.draw();
console.log(r.getPerimeter());
console.log(r.getArea());//正方形
var s = new Square(new Point(130, 130), 50);
s.draw();
console.log(s.getPerimeter());
console.log(s.getArea());new Square(p1, 200).draw();

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/61.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

css之:is()、:where()和:has()伪元素的运用、使用、important

文章目录 简介1、:is()2、:where()3、:has() 简介 :is()、:where()和:has()伪元素是CSS中用于样式化元素的非常强大的工具。它们是在CSS选择器Level4规范中引入的。它们允许我们将样式应用于符合特定条件的任何元素&#xff0c;例如元素的类型、元素的位置和元素的后代。 1、:i…

【高并发】高并发架构实战:从需求分析到系统设计

Yan-英杰的主页 悟已往之不谏 知来者之可追 C程序员&#xff0c;2024届电子信息研究生 很多软件工程师的职业规划是成为架构师&#xff0c;但是要成为架构师很多时候要求先有架构设计经验&#xff0c;而不做架构师又怎么会有架构设计经验呢&#xff1f;那么要如何获得架构设…

前端渲染模式CSR,SSR,SSG,ISR,DPR

目录 一、客户端渲染——CSR&#xff08;Client Side Rendering&#xff09; 二、服务器端渲染——SSR&#xff08;Server Side Rendering&#xff09; 三、静态站点生成——SSG&#xff08;Static Site Generation&#xff09; 四、增量静态生成——ISR&#xff08;Increm…

【软件测试】在Windows环境安装Docker(详细步骤)

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 下载和安装 1、地…

智慧园区4G+蓝牙+GPS/北斗RTK人员定位系统解决方案

智慧园区是指利用现代科技手段进行精细化管理的园区&#xff0c;人员定位技术被广泛应用在智慧园区。智慧园区人员定位技术通过使用传感器设备&#xff0c;能够实时监测园区内人员的位置和活动情况&#xff0c;从而提高园区的人员管理效率和安全性。 通过人员定位&#xff0c;…

云HIS是什么?HIS系统为什么要上云?云HIS有哪些优点?

一、当前医疗行业HIS的现状与发展趋势 1.医院信息系统&#xff08;HIS&#xff09;经历了从手工到单机再到局域网的两个阶段&#xff0c;随着云计算、大数据新技术迅猛发展&#xff0c;基于云计算的医院信息系统将逐步取代传统局域网HIS , 以适应人们对医疗卫生服务越来越高的要…

常用分类损失CE Loss、Focal Loss及GHMC Loss理解与总结

一、CE Loss 定义 交叉熵损失&#xff08;Cross-Entropy Loss&#xff0c;CE Loss&#xff09;能够衡量同一个随机变量中的两个不同概率分布的差异程度&#xff0c;当两个概率分布越接近时&#xff0c;交叉熵损失越小&#xff0c;表示模型预测结果越准确。 公式 二分类 二…

深入理解预训练(pre-learning)、微调(fine-tuning)、迁移学习(transfer learning)三者的联系与区别

1. 什么是预训练和微调 你需要搭建一个网络模型来完成一个特定的图像分类的任务。首先&#xff0c;你需要随机初始化参数&#xff0c;然后开始训练网络&#xff0c;不断调整参数&#xff0c;直到网络的损失越来越小。在训练的过程中&#xff0c;一开始初始化的参数会不断变化。…

小程序:页面跳转闪屏

自己的笔记&#xff0c;随手记录。扛精走开。 1、问题描述 进入页面&#xff0c;是一个组件&#xff0c;通过路由传参判断是由哪个页面进入&#xff0c;不同的页面拿的已选值不一样&#xff0c;需要回显值&#xff0c;在编辑数据。此时会出现一个问题&#xff0c;A页面中进来…

Serverless和EDA是绝配,亚马逊云科技CTO Werner表示需要用开放心态来重新审视架构

前一段有个很火的博客&#xff0c;讲的是一家全球流媒体企业的监测系统从Serverless微服务改成了单体&#xff0c;成本居然降低了90%&#xff01;这一下子可在网上炸锅了&#xff0c;特别是一些看不惯微服务的、单体应用的拥趸&#xff0c;更是坐不住了。但这并不像吃瓜群众看到…

【ECharts系列】ECharts 图表渲染问题解决方案

1 问题描述 echats 渲染&#xff0c;第一次的时候只出现Y轴数值&#xff0c;不出现X轴数值&#xff0c;切换下页面&#xff0c;X轴数值就能出现。 2 原因分析 如果在使用ECharts渲染时&#xff0c;X轴数值只在切换页面后才出现&#xff0c;可能是因为ECharts在初始化时没有正确…

光速吟唱,Clibor ,批量多次复制依次粘贴工具 快捷输入软件教程

批量多次复制依次粘贴工具 批量复制粘贴工具0.81.exe https://www.aliyundrive.com/s/3sbBaGmHkb8 点击链接保存&#xff0c;或者复制本段内容&#xff0c;打开「阿里云盘」APP &#xff0c;无需下载极速在线查看&#xff0c;视频原画倍速播放。 青县solidworks钣金设计培训 …

Redis进阶 - Redis哨兵

原文首更地址&#xff0c;阅读效果更佳&#xff01; Redis进阶 - Redis哨兵 | CoderMast编程桅杆https://www.codermast.com/database/redis/redis-advance-sentinel.html 思考 slave 节点宕机恢复以后可以找 master 节点同步数据&#xff0c;那么 master 节点宕机怎么办&am…

[Docker] Docker镜像管理和操作实践(二) 文末送书

前言&#xff1a; Docker镜像是容器化应用程序的打包和分发单元&#xff0c;包含了应用程序及其所有依赖项&#xff0c;实现了应用程序的可移植性和一致性。 文章目录 使用Dockerfile创建自定义镜像实践练手1. 创建基于ubuntu的自定义镜像&#xff0c;并安装nginx2. 配置Redis容…

MySQL数据库高级查询语句

MySQL数据库高级查询语句 一、语句SELECT ----显示表格中一个或数个字段的所有数据记录DISTINCT ----不显示重复的数据记录WHERE ----有条件查询AND OR ----且 或IN ----显示已知的值的数据记录BETWEEN ----显示两个值范围内的数据记录通配符 ----通常通配符都是跟 LIKE 一起使…

【云原生】二进制k8s集群(下)部署高可用master节点

本次部署说明 在上一篇文章中&#xff0c;就已经完成了二进制k8s集群部署的搭建&#xff0c;但是单机master并不适用于企业的实际运用&#xff08;因为单机master中&#xff0c;仅仅只有一台master作为节点服务器的调度指挥&#xff0c;一旦宕机。就意味着整个集群的瘫痪&#…

宝塔Linux面板安装Composer依赖管理工具与PHP依赖包的方法

最近看见腾讯云有一个AI绘画还挺有意思&#xff0c;想搞来写个接口玩 但是Composer一直运行不成功 提示xdebug什么的 最后经过搜索 发现 需要删除你宝塔里所有php中禁用的putenv函数 然后重启php就可以了&#xff01; 然后就可以运行这个命令了 出现这种情况 还需要删除所有…

Linux常用命令——emacs命令

在线Linux命令查询工具 emacs 功能强大的全屏文本编辑器 补充说明 emacs命令是由GNU组织的创始人Richard Stallman开发的一个功能强大的全屏文本编辑器&#xff0c;它支持多种编程语言&#xff0c;具有很多优良的特性。有众多的系统管理员和软件开发者使用emacs。 语法 e…

服务器技术(三)--Nginx

Nginx介绍 Nginx是什么、适用场景 Nginx是一个高性能的HTTP和反向代理服务器&#xff0c;特点是占有内存少&#xff0c;并发能力强&#xff0c;事实上nginx的并发能力确实在同类型的网页服务器中表现较好。 Nginx专为性能优化而开发&#xff0c;性能是其最重要的考量&#xf…

【Nginx】rewrite简单使用

前言 没有对正式的rewrite进行了解&#xff0c;为了能快速了解它是干嘛怎么用&#xff0c;找了一些有例子的博客进行简单学习了一下&#xff1b;由于每次看的间隔有点大&#xff0c;老忘记&#xff0c;这回专门写个超级快速理解的例子。 PS&#xff1a;下面的解释可能会不太对…