Classical Inheritance in JavaScript
Douglas Crockford www.crockford.com 翻译:ThoughtScript 原文链接 http://javascript.crockford.com/inheritance.html
And you think you're so clever and classless and free — John Lennon
JavaScript 一种是与类无关的面向对象语言(基于对象),因此Javascript使用了基于原型的方式(原型链)代替传统面向对象语言的继承方式。这可能会让过去习惯于使用C++、Java这类面向对象语言继承方式的程序员感到困惑。但马上你就能认识到实际上Javascript的原型方式确实比传统的方式更能表达继承关系。
Java
JavaScript
Strongly-typed |
Loosely-typed |
Static |
Dynamic |
Classical |
Prototypal |
Classes |
Functions |
Constructors |
Functions |
Methods |
Functions |
但是,首先我们需要思考我们使用继承的目的是什么?有两个主要的方面。第一是出于类型转换。我们希望语言环境能自动的对引用类的进行类型转换,但有时是无法通过类型系统的安全检查的,所以通常我们需要会对引用的对象进行显示转换,这对强类型语言至关重要,但是对像JavaScript这样弱类型语言却是无关紧要,因为Javascript中对象的引用不需要类型转换。
第二是考虑代码重用。通常我们通过使一系列不同对象实现相同的接口来达到重用。类继承方式通过创建一个单独的定义集合接口让重用变的可能,通常我们看到一些对象非常相似,但是对象间也存在区别,主要是增加或修改了一小部分方法。传统的类继承方式对代码重用的确有用,但JavaScript的原型方式更有用。
为了证明这点,我们将会介绍一些 sugar 让我们的代码形式类似于传统语言的形式。并且我们将展示在传统语言中不可行但却非常有用的模式,最后,我们将解释这些 sugar。
Classical Inheritance 类继承方式
首先,我们定义一个Parenizor类,Parenizor拥有设置和获取value变量的方法,和一个返回把value用括号包裹起来后值的toString方法。
function Parenizor(value) {
this.setValue(value);
}
Parenizor.method('setValue', function (value) {
this.value = value;
return this;
});
Parenizor.method('getValue', function () {
return this.value;
});
Parenizor.method('toString', function () {
return '(' + this.getValue() + ')';
});
这样的语法看上去有些不寻常,但这是可以很容易让人识别这是传统类继承的模式。method方法接收一个方法名称和一个方法函数,让方法作为公共方法添加到类中。
所以现在我们可以这样写:
myParenizor = new Parenizor(0);
myString = myParenizor.toString();
正如您所期望的,myString的值为“(0)”
现在我们定义另一个继承自Parenizor的类,同样的toString方法但返回“-0-”如果值为零或空。
function ZParenizor(value) {
this.setValue(value);
}
ZParenizor.inherits(Parenizor);
ZParenizor.method('toString', function () {
if (this.getValue()) {
return this.uber('toString');
}
return "-0-";
});
inherits方法与Java的extends关键字作用很相似,uber方法与Java的super相似,允许方法调用父类的方法。(该名称已被更改,以避免保留字的限制。)
所以现在我们可以这样写:
myZParenizor = new ZParenizor(0);
myString = myZParenizor.toString();
这次,myString的值为“-0-”
Javascript没有类的概念,但是我们依旧可以按类方式的编程。
Multiple Inheritance 多重继承
通过操纵一个函数的原型对象,我们可以实现多重继承,使我们能创建一个继承多个类的不同方法。
复杂的多重继承难以实现,并有可能遭受潜在的方法名称冲突。我们可以在JavaScript中实现复杂的多重继承,但对于这个例子中,我们将使用一种称为Swiss Inheritance的更严格形式。
假设有一个NumberValue的类,具有 一个setValue 方法用来检查value变量是否是指定范围内的数字,并在需要的时候可以抛出异常。我们只需要ZParenizor的 setValue和setRange方法,而不需要toString方法,所以我们可以这样写:
ZParenizor.swiss(NumberValue, 'setValue', 'setRange');
这里只把我们请求的方法添加到了ZParenizor类中。
Parasitic Inheritance 寄生继承
这里用另一种代替继承自Parenizor的方法来定义ZParenizor。寄生继承并不直接从 Parenizor继承,而是通过在ZParenizor构造器内部中调用Parenizor构造器,通过对Parenizor构造器返回的结果that进行修改,最后返回修改后的that。而这个构造器添加的是特权方法(privileged methods)而非公共方法。
function ZParenizor2(value) {
var that = new Parenizor(value);
that.toString = function () {
if (this.getValue()) {
return this.uber('toString');
}
return "-0-"
};
return that;
}
类继承方式是 is-a(是...) 的关系,而寄生继承是 was-a-but-now's-a(原来是……而现在是……) 的关系。在对象构造中构造器扮演了主要的角色。注意uber方法(代替super保留字)对特权方法仍然有效。
Class Augmentation 类扩展
JavaScript的动态特性使我们能够添加或替换现有类中的方法。我们能在任何时候调用method方法,不管现有的还是未来创建的类实例都将会含有新添加的方法,这样我们在随时都能很直接的扩展类的方法。而继承是通过追溯的方式工作的,和类扩展是不同的。我们把这种方式命名为“Class Augmentation ”是因为想避免Java中的extends(扩展)混淆,这是两个完全不同的概念。
Object Augmentation 对象扩展
在静态的面向对象语言中,假如你需要一个只是与另一已经存在对象稍有不同的对象,你必须重新定义一个新的类。但在Javascript,你可以添加单个对象的方法,而不需要定义额外的类。这样你可以写更少更简单的类,这是Javascript非常强大的特性。把每一个Javascript对象现象成一张张哈希表,你可以随时添加新的变量到哈希表中,如何变量是函数,变量就会成为对象的方法。
这样在上面的例子中,我完全不需要用到ZParenizor类,就能以很简单的方式修改实例对象来达到扩展对象。
myParenizor = new Parenizor(0);
myParenizor.toString = function () {
if (this.getValue()) {
return this.uber('toString');
}
return "-0-";
};
myString = myParenizor.toString();
这里我们没有使用任何形式的继承就增加了 toString方法到myParenizor 实例对象中。这是因为JavaScript是与类无关的语言,所以你可以扩展每一个独立的实例对象。
Sugar
为了让上面的例子能运行,我写了四个sugar 方法。第一个是method方法,用来把实例方法添加到类中。
Function.prototype.method = function (name, func) {
this.prototype[name] = func;
return this;
};
这里添加了一个公共的方法到Function.prototype,这里通过类扩展的方式这样所有的方法都可以使用method方法。这你需要一个函数名称和函数,作为参数,将它们添加到一个函数的原型对象中。
这里返回本身(this)。当我写一个方法,它不需要返回值时,我通常会返回它本身,这样可以允许写成链式语句的编程风格。
下一个是inherits方法,用来让一个类继承另一个类。inherits方法应该在两个类定义之后调用,但在添加继承方法之前。
Function.method('inherits', function (parent) {
var d = {}, p = (this.prototype = new parent());
this.method('uber', function uber(name) {
if (!(name in d)) {
d[name] = 0;
}
var f, r, t = d[name], v = parent.prototype;
if (t) {
while (t) {
v = v.constructor.prototype;
t -= 1;
}
f = v[name];
} else {
f = p[name];
if (f == this[name]) {
f = v[name];
}
}
d[name] += 1;
r = f.apply(this, Array.prototype.slice.apply(arguments, [1]));
d[name] -= 1;
return r;
});
return this;
});
再来,我们扩展Function类。我们创建parent类的实例对象做为新的prototype。在添加uber方法到prototype的同时修正constructor 字段。
uber方法将会在自己的prototype中查找指定的方法。这个方法在寄生继承或对象扩展的情况下会被调用执行。如果使用的是类继承方式,那么我们要找到parent的prototype中的函数。return语句调用了函数的apply方法来调用该函数,同时显示地设置this并传递一个参数数组。参数(如果有的话)可以从arguments数组中获得。不幸的是,arguments数组并不是一个真正的数组,所以我们必须再次调用apply来调用数组中的slice方法。
最后是swiss方法:
Function.method('swiss', function (parent) {
for (var i = 1; i < arguments.length; i += 1) {
var name = arguments[i];
this.prototype[name] = parent.prototype[name];
}
return this;
});
swiss方法遍历arguments参数对象。通过每一个参数名称name,他从parent原型中拷贝成员到自身类的原型中。
Conclusion 结论
JavaScript可以传统语言方式那样使用,但它也有丰富独特的表达继承的方式。我们已经了解了 类继承方式,Swiss继承方式,寄生继承方式,类扩展和对象扩展。这一系列代码重用的模式来自被认为比Java更小,更简单的Javascript。
传统语言中的对象是固定的(hard),要在对象中添加新的成员变量的唯一方式就是创建新的类。但在Javascript中,对象是灵活(soft),添加一个新的成员到JavaScript对象中只需要作简单的赋值。
由于JavaScript中的对象是如此灵活,你必须对类层次结构有不同的认识。深层次(Deep hierarchies )是不适当的,浅层次(Shallow hierarchies )更具执行效率和可读性。
到现在我已经写了8年的JavaScript,期间我发现我从来没有使用过uber方法。调用父类成员(super)的想法在传统模式中是相当重要的,但是在原型和函数式的模式中这看起来并不是很有必要。从现在看来早期我支持传统模式中super的尝试是一个错误。
相关推荐
Classical Inheritance in JavaScript。 Crockford是JavaScript开发社区最知名的权威,是JSON、JSLint、JSMin和ADSafe之父,是《[removed] The Good Parts》的作者。 现在是Yahoo的资深JavaScript架构师,参与YUI的...
本人一行注释一行代码翻译了该大师的艺术作品--目的说明它是在第1,2阶段文档演示的JavaScript面向对象的书写方式的进一步改进,它是现代JavaScript面向对象编程方式(使用基本类来编码)的过渡代码--没有它就没有当今...
基于万能逼近原理的自适应模糊控制算法在多自由度AUV运动控制中的应用与抗干扰补偿Simulink仿真研究,自适应模糊控制算法的万能逼近原理与多自由度AUV运动控制的抗干扰补偿技术——基于Simulink的仿真研究,万能逼近原理自适应模糊控制算法的多自由度AUV运动控制抗干扰补偿simulink仿真 ,核心关键词:万能逼近原理; 自适应模糊控制算法; 多自由度AUV运动控制; 抗干扰补偿; Simulink仿真。,基于万能逼近的模糊控制算法多自由度AUV抗干扰补偿Simulink仿真
deepseek最新资讯、配置方法、使用技巧,持续更新中
deepseek最新资讯、配置方法、使用技巧,持续更新中
结合扩展卡尔曼滤波与滑模观测器的策略:优化电角度估计,反电势波形逼近完美正弦波,结合扩展卡尔曼滤波与滑模观测器的反电势波形优化:正弦波形展现近乎完美精度,电角度估算与实际应用差异微小,扩展卡尔曼滤波与滑模观测器的结合,反电势波形近乎完美的正弦波形,观测器估算转子电角度与实际电角度相差0.3弧度左右,转速跟随效果较好。 ,核心关键词:扩展卡尔曼滤波; 滑模观测器; 反电势波形; 转子电角度估算; 转速跟随效果。,卡尔曼滑模观测器:优化正弦波转子角度与转速估算
毕业设计_基于springboot+vue的**学生公寓管理系统**【源码+sql+可运行】【**50217**】.zip 全部代码均可运行,亲测可用,尽我所能,为你服务; 1.代码压缩包内容 代码:springboo后端代码+vue前端页面代码; 脚本:数据库SQL脚本 效果图:运行结果请看资源详情效果图 2.环境准备: - JDK1.8+ - maven3.6+ - nodejs14+ - mysql5.6+ - redis 3.技术栈 - 后台:springboot+mybatisPlus+Shiro - 前台:vue+iview+Vuex+Axios - 开发工具: idea、navicate 4.功能列表 - 系统设置:用户管理、角色管理、资源管理、系统日志 - **业务管理:业务管理:公寓信息、房间信息、入住记录、学生信息** 3.运行步骤: 步骤一:修改数据库连接信息(ip、port修改) 步骤二:找到启动类xxxApplication启动 4.若不会,可私信博主!!!
1、文件内容:xorg-x11-server-source-1.20.4-29.el7_9.rpm以及相关依赖 2、文件形式:tar.gz压缩包 3、安装指令: #Step1、解压 tar -zxvf /mnt/data/output/xorg-x11-server-source-1.20.4-29.el7_9.tar.gz #Step2、进入解压后的目录,执行安装 sudo rpm -ivh *.rpm 4、更多资源/技术支持:公众号禅静编程坊
1、文件内容:yum-plugin-ps-1.1.31-54.el7_8.rpm以及相关依赖 2、文件形式:tar.gz压缩包 3、安装指令: #Step1、解压 tar -zxvf /mnt/data/output/yum-plugin-ps-1.1.31-54.el7_8.tar.gz #Step2、进入解压后的目录,执行安装 sudo rpm -ivh *.rpm 4、更多资源/技术支持:公众号禅静编程坊
基于模型预测控制(MPC)的无人船与无人车编队一致性协同控制研究(附原文献),基于模型预测控制(MPC)的无人船与无人车编队一致性协同控制研究(附原文献),无人船编队 无人车编队 MPC 模型预测控制 多智能体协同控制 一致性 MATLAB 无人车 USV 带原文献 ,无人船编队; 无人车编队; MPC 模型预测控制; 多智能体协同控制; 一致性; MATLAB; USV; 原文献,无人系统协同控制:MPC模型预测控制下的多智能体编队与一致性研究(原文献支撑)
4套中级通信工程师综合真题及答案(2019,2020,2021,2023),适用于需要考中级通信工程师的人群
deepseek最新资讯,配置方法,使用技巧,持续更新中
基于matlab的锁相环PLL相位噪声拟合仿真代码集合:多个版本建模与仿真,高质量的锁相环PLL仿真代码集合:Matlab与Simulink建模研究,[1]锁相环 PLL 几个版本的matlab相位噪声拟合仿真代码,质量杠杠的,都是好东西 [2]锁相环matlab建模稳定性仿真,好几个版本 [3]锁相环2.4G小数分频 simulink建模仿真 ,PLL; Matlab相位噪声拟合仿真; Matlab建模稳定性仿真; 锁相环2.4G小数分频Simulink建模仿真,MATLAB仿真系列:锁相环PLL及分频器建模仿真
exceptionLogs.zip
基于光伏微网的经济性与并网负荷波动率双目标优化调度策略:蓄电池与V2G协同管理策略仿真研究,MATLAB下光储充微网结合电动汽车V2G的多目标协同调度策略研究:经济性与并网负荷波动性的对比分析,MATLAB代码:考虑V2G的光储充一体化微网多目标优化调度策略 关键词:光储充微网 电电汽车V2G 多目标优化 蓄电池优化 调度 参考文档:《光伏微网下考虑V2G补偿蓄电池容量的双目标优化调度策略》,已经投稿EI会议,中文说明文档可联系我咨询 仿真平台:MATLAB 平台 优势:代码注释详实,适合参考学习,相关成果已经采用,程序非常精品,请仔细辨识 主要内容:过建立光伏微网中以经济性和并网负荷波动率为双目标的蓄电池和V2G的协同调度模型。 采用粒子群算法,对电网、微网调度中心和电动汽车用户三方在无、无序、转移和调度V2G电动汽车负荷四种运行模式下的经济和安全影响进行对比。 最后,根据算例分析,求解四种模式下两级负荷曲线及经济收益表。 对比分析得出,引入V2G可以替代部分容量的蓄电池,使光伏微网在负荷峰谷平抑、三方经济和安全等方面进一步优化。 求解采用的是PSO算法(粒子群算法),求解效果极
javascript 动态网页设计期末大作业(自己手写的,高分期末作业),含有代码注释,新手也可看懂,个人手打98分项目,导师非常认可的高分项目,毕业设计、期末大作业和课程设计高分必看,下载下来,简单部署,就可以使用。该项目可以直接作为毕设、期末大作业使用,代码都在里面,系统功能完善、界面美观、操作简单、功能齐全、管理便捷,具有很高的实际应用价值,项目都经过严格调试,确保可以运行! javascript 动态网页设计期末大作业(自己手写的,高分期末作业)javascript 动态网页设计期末大作业(自己手写的,高分期末作业)javascript 动态网页设计期末大作业(自己手写的,高分期末作业)javascript 动态网页设计期末大作业(自己手写的,高分期末作业)javascript 动态网页设计期末大作业(自己手写的,高分期末作业)javascript 动态网页设计期末大作业(自己手写的,高分期末作业)javascript 动态网页设计期末大作业(自己手写的,高分期末作业)javascript 动态网页设计期末大作业(自己手写的,高分期末作业)javascript 动态网页设计期
混合智能体系统编队控制:分布式优化与15异构混合阶的挑战,异构混合阶智能体系统编队控制的分布式优化策略研究,15异构混合阶多智能体系统编队控制的分布式优化(无参考文献) ,核心关键词:15异构混合阶; 多智能体系统; 编队控制; 分布式优化; 无参考文献。,15混合阶多智能体系统编队分布式优化控制
javascript 动态网页设计期末大作业(自己手写的,很适合期末作业),含有代码注释,新手也可看懂,个人手打98分项目,导师非常认可的高分项目,毕业设计、期末大作业和课程设计高分必看,下载下来,简单部署,就可以使用。该项目可以直接作为毕设、期末大作业使用,代码都在里面,系统功能完善、界面美观、操作简单、功能齐全、管理便捷,具有很高的实际应用价值,项目都经过严格调试,确保可以运行! javascript 动态网页设计期末大作业(自己手写的,很适合期末作业)javascript 动态网页设计期末大作业(自己手写的,很适合期末作业)javascript 动态网页设计期末大作业(自己手写的,很适合期末作业)javascript 动态网页设计期末大作业(自己手写的,很适合期末作业)javascript 动态网页设计期末大作业(自己手写的,很适合期末作业)javascript 动态网页设计期末大作业(自己手写的,很适合期末作业)javascript 动态网页设计期末大作业(自己手写的,很适合期末作业)javascript 动态网页设计期末大作业(自己手写的,很适合期末作业)javascrip
X光安检OPIXray数据集已经转换为VOC格式,可直接转换为为YOLO
DataX--Web:图形化界面简化大数据任务管理_datax-web