当前位置: 首页 > 编程日记 > 正文

es5 编写类风格的代码

分享下《JavaScript忍者秘籍》中的一种编写类风格代码的方法

JavaScript可以让我们通过原型实现继承,许多开发人员,尤其是那些有传统面向对象背景的开发人员,都希望将JavaScript的继承系统简化并抽象成一个他们更熟悉的系统。
所以,这不可避免地引导我们走向类的领域。类是面向对象开发人员所期望的内容,尽管JavaScript本身不支持传统的类继承。
通常,这些开发人员希望它有如下特性:

  • 一套可以构建新构造器函数和原型的轻量级系统
  • 一种简单的方式来执行原型继承
  • 一种可以访问被函数原型所覆盖的方法的途径

以下代码展示了一个可以实现上述目标的示例。

//通过subClass()方法,创建一个Person类作为Object的一个子类,该方法之后实现
var Person = Object.subClass({init: function (isDancing) {this.dancing = isDancing;},dance: function () {return this.dancing;}
});//通过继承Person类,创建一个Ninja子类
var Ninja = Person.subClass({init: function () {
//需要一种调用父类构造器的方法——这里展示我们将这样做this._super(false);},dance: function () {//Ninja-specific stuff herereturn this._super();},swingSword: function () {return true;}
});//创建一个实例对Person类进行测试,看其是否能够跳舞
var person = new Person(true);
assert(person.dance(),"The person is dancing.");//创建一个实例对Ninja类进行测试,看其是否有swingSword方法以及继承过来的dance方法
var ninja = new Ninja();
assert(ninja.swingSword(),"The sword is swinging.");
assert(!ninja.dance(),"The ninja is not dancing.");//执行instanceof测试,验证类的继承
assert(person instanceof Person,"Person is a Person");
assert(ninja instanceof Ninja && ninja instanceof Person,"Ninja is a Ninja and a Person");复制代码

注意事项:

  • 通过调用现有构造器函数的subClass()方法可以创建一个新“类”,例如,通过Object创建一个Person类,以及通过Person创建一个Ninja类
  • 为了让构造器的创建更加简单。我们建议的语法是,为每个类只提供一个init()方法,就像为Person和Ninja提供的init()方法一样
  • 我们所有的“类”最终都继承于一个祖先:Object。因此,如果要创建一个新类,它必须是Object的一个子类,或者是一个在层级上继承于Object的类(完全模仿当前的原型系统)
  • 该语法的最大挑战是访问被覆盖的方法,而且有时这些方法的上下文也有可能被修改了。通过this._super()调用Person的原始init()和dance()方法,我们就可以了解这种用法

实现:

(function () {var initializing = false,
//粗糙的正则表达式用于判断函数是否可以被序列化。superPattern =/xyz/.test(function () {xyz;})?/\b_super\b/: /.*/;//给Object添加一个subClass方法Object.subClass = function (properties) {var _super = this.prototype;//初始化超类initializing = true;var proto = new this();initializing = false;for (var name in properties) {//将属性复制到prototype里proto[name] = typeof properties[name] === 'function' &&typeof _super[name] === 'function' &&superPattern.test(properties[name]) ?//定义一个重载函数(function (name, fn) {return function () {var tmp = this._super;this._super = _super[name];var ret = fn.apply(this, arguments);this._super = tmp;return ret;}})(name, properties[name]) :properties[name];}//创造一个仿真类构造器function Class() {if (!initializing && this.init) {this.init.apply(this, arguments);}}//设置类的原型Class.prototype = proto;//重载构造器引用Class.constructor = Class;//让类继续可扩展Class.subClass = arguments.callee;return Class;};
})();复制代码

检测函数是否可序列化

代码实现的一开始就很深奥,而且还可能让人困惑。在后续代码中,我们需要知道浏览器是否支持函数序列化。但该测试又有相当复杂的语法,所以现在就要得到结果,然后保存结果,以便在后续代码中不再进行复杂的操作,因为后续代码本身已经够复杂了。
函数序列化就是简单接收一个函数,然后返回该函数的源码文本。稍后,我们可以使用这种方法检查一个函数在我们感兴趣的对象中是否存在引用。
在大多数浏览器中,函数的toString()方法都会奏效。一般来说 ,一个函数在其上下文中序列化成字符串,会导致它的toString()方法被调用。所以,可以用这种方法测试函数是否可以序列化。
在设置一个名为initializing的变量为false之后,我们使用如下表达式测试一个函数是否能够被序列化:

/xyz/.test(function () { xyz; })复制代码

该表达式创建一个包含xyz的函数,将该函数传递给正则表达式的test()方法,该正则表达式对字符串“xyz”进行测试。如果函数能够正常序列化(test()方法将接收一个字符串,然后将触发函数的toString()方法),最终结果将返回true。
使用该文本表达式,我们在随后的代码中使用了该正则表达式:

superPattern =/xyz/.test(function () {xyz;}) ?/\b_super\b/ :/.*/;复制代码

建立了一个名为superPattern的变量,稍后用它来判断一个函数是否包含字符串"_super"。只有函数支持序列化才能进行判断,所以在不支持序列化的浏览器上,我们使用一个匹配任意字符串的模式进行代替。

子类的实例化

此时,我们准备开始定义一个方法用于子类化父类,我们使用如下代码进行实现:

Object.subClass = function (properties) {var _super = this.prototype;复制代码

给Object添加一个subClass()方法,该方法接收一个参数,该参数是我们期望添加到子类的属性集。
为了用函数原型模拟继承,我们创建父类的一个实例,并将其赋值给子类的原型。我们在代码中定义了一个initializing变量,每当我们想使用原型实例化一个类的时候,都将该变量设置为true。
因此,在构造实例时,我们可以确保不再实例化模式下进行构建实例,并可以相应地运行或跳过init()方法:

if (!initializing && this.init) {this.init.apply(this, arguments);
}复制代码

尤其重要的是,init()方法可以运行各种昂贵的启动代码(连接到服务器、创建DOM元素,还有其他未知内容),所以如果只是创建一个实例作为原型的话,我们要避免任何不必要的昂贵启动代码。

保留父级方法

大多数支持继承的语言中,在一个方法被覆盖时,我们保留了访问被覆盖方法的能力。这是很有用的,因为有时候我们是想完全替换方法的功能,但有时候我们却只是想增加它。在我们特定的实现中,我们创建一个名为_super的临时新方法,该方法只能从子类方法内部进行访问,并且该方法引用的是父类中的原有方法。
例如:

var Person = Object.subClass({init: function (isDancing) {this.dancing = isDancing;}
});var Ninja = Person.subClass({init: function () {this._super(false);}
});复制代码

在Ninja构造器内,我们调用了Person的构造器,并传入了一个相应的值。这可以防止重新复制代码——我们可以重用父类中已经编写好的代码。
该功能的实现是一个多步骤的过程。为了增强子类,我们向subClass()方法传入了一个对象哈希,只需要将父类的属性和传入的属性合并在一起就可以了。
首先,使用如下代码,创建一个超类的实例作为一个原型:

initializing = true;
var proto = new this();
initializing = false;复制代码

注意,我们是如何“保护”初始化代码的,正如我们在前一节中讨论的initializing变量的值。
现在,是时候将传入的属性合并到proto对象中了。如果不在意父类函数,合并代码将非常简单:

for (var name in properties)  {proto[name] = properties[name];
}复制代码

但是,我们需要关心父类的函数,所以前面的代码和除了调用父类函数的函数之外是等价的。重写函数时,可以通过_super调用父函数,我们需要通过名为_super的属性,将子类函数和父类函数的引用进行包装。但在完成该操作之前,我们需要检测即将被包装的子类函数。可以使用如下条件表达式:

typeof properties[name] === "function" &&
typeof _super[name] === "function" &&
superPattern.test( properties[name] )复制代码

这个表达式包含三个检测条件:

  • 子类属性是否是一个函数?
  • 超类属性是否是一个函数?
  • 子类函数是否都包含一个_super()引用?

只有三个条件都为true的时候,我们才能做所要做的事情,而不是复制属性值。注意,我们使用了之前设置的正则表达式,和函数序列化一起,测试函数是否会调用等效的父类。
如果条件表达式表明我们必须包装功能,我们通过给即时函数的结果进行赋值,将该结果作为子类的属性:

(function (name, fn) {return function () {var tmp = this._super;this._super = _super[name];var ret = fn.apply(this, arguments);this._super = tmp;return ret;}
})(name, properties[name])复制代码

该即时函数创建并返回了一个新函数,该新函数包装并执行了子类的函数,同时可以通过_super属性访问父函数。首先需要先保持旧的this._super引用(不管它是否存在),然后处理完以后再恢复该引用。这在同名变量已经存在的情况下会很有用(不想意外的丢失它)。
接下来,创建新的_super方法,它只是在父类原型中已经存在的一个方法的引用。值得庆幸的是,我们不需要做任何额外的代码修改或作用域修改。当函数成为我们对象的一个属性时,该函数的上下文会自动设置(this引用的是当前的子类实例,而不是父类实例)。
最后,调用原始的子类方法执行自己的工作(也有可能使用了_super),然后将_super恢复成原来的状态,并将方法调用结果进行返回。
有很多方式可以达到类似的结果(有的实现,会通过访问arguments.callee,将_super方法绑定到方法自身),但是该特定技术提供了良好的可用性和简便性。

相关文章:

与15级新加入团队同学的约定

坚持!坚持!坚持!

【冷门实用小工具】轻量级流程图工具ClickCharts PRO绿色版,ClickCharts PRO下载【亲测有效】

下载地址:ClickCharts Pro注册版下载地址 轻量级流程图工具ClickCharts Pro注册版本,大小总共900多K,功能跟visio一样强大。 一、实用功能: 1.创建图表模板 2.可以选择各种符号和线连接风格 3.创建UML(统一建模语言)标准的可视化…

深入讨论.NET Socket的Accept方法

深入讨论.NET Socket的Accept方法 考虑一个问题,假如同时有50个连接请求进入一个服务器(这种情况对于普通负载的Web服务器都是很常见的)会怎么样?阻塞式I/O只能循环调用Accept,一个一个对50个连接进行Accept操作&…

php代码中使用换行及(\n或\r\n和br)的应用

浏览器识别不了\n或\r\n&#xff0c;这两个换行符是文本换行符&#xff0c;文本文件有效;假设须要将结果输出到浏览器或打印到显示器&#xff0c;代码中使用br;假设仅仅是在源码中换行。则使用\n或\r\n,感兴趣的朋友能够了解下&#xff0c;也许对你学习php有所帮助 <?php e…

前后端分离的探索(一)

文桥&#xff0c;13级机械系学生。在LSGO软件技术团队负责前端部分&#xff0c;大家或许会惊讶&#xff0c;怎么一个Coding高手&#xff0c;非计算机或数学出身。让我告诉你一个秘密&#xff1a;如果你花时间去追踪技术圈内绝大多数的大牛、大神的背景&#xff0c;甚至跟他们聊…

【冷门实用小工具】JAVA和C#轻量级的UML图绘制工具NClass,UML类图编辑器免安装版【亲测有效】

下载地址&#xff1a;NClass下载地址 NClass是一款免费的UML图绘制工具&#xff0c;它很小巧轻量级&#xff0c;解压之后大小不到2M&#xff0c;便于携带和使用&#xff0c;是很实用UML绘制工具。 功能介绍&#xff1a; 1、支持完整的C&#xff03;和Java支持多语言的特定元素…

C#读取XML点滴

今天遇到了一个xml属性值读取的问题&#xff0c;我先是从代码的思路、语法查找问题&#xff0c;都没有发现错误&#xff0c; 接着我又从客户端和服务器端应答的信息找问题&#xff0c;原来问题出在我取的xml属性值没有出现在这个xml字符串里&#xff08;此处所说的xml字符串是根…

Bash之break、continue和return命令在循环中的作用

1 continue&#xff1a;直接跳过本次循环&#xff0c;进入下一次循环。#!/bin/basha10b15while [ $a -le $b ]do((a))if [ $a -eq 11 ] || [ $a -eq 13 ]thencontinuefiecho $adone[rootmaster ~]# ./a.sh121415162 break&#xff1a;此命令将会跳出循环#!/bin/basha8b15while …

分析与设计(AD)简介(1)

这一节主要介绍在RUP的分析与设计阶段所涉及的&#xff1a;Role、Activity、Workflow、Artifact。

混淆梯度(Obfuscated Gradients Give a False Sense of Security Circumventing Defense)

下载地址&#xff1a;https://u20150046.ctfile.com/fs/20150046-376633529 论文摘要 我们发现“混淆梯度”&#xff08;obfuscated gradients&#xff09;作为一种梯度掩码&#xff08;gradient masking&#xff09;&#xff0c;会在防御对抗样本中导致一种错误的安全感。虽然…

在jsp中点击按钮,在bean中把已经查出的数据,生成csv文件,然后在ie中自动打开

Quote:这个问题可以分两部分讨论&#xff1a; 1、csv文件的格式 2、通过jsp向客户端输出csv文件 第一个问题我们就按简单的来讨论&#xff0c;可以认为是每个字段用""包含后再用,号分割 比如&#xff1a; "a","b","c" "d&q…

分析与设计(AD)简介(2)

这一节主要介绍OOAD分析与设计中涉及的几个重要概念&#xff0c;包括&#xff1a;分析、设计、体系结构、41视图、Use Case实现等。

1.3 Quick Start中 Step 7: Use Kafka Connect to import/export data官网剖析(博主推荐)

不多说&#xff0c;直接上干货&#xff01; 一切来源于官网 http://kafka.apache.org/documentation/ Step 7: Use Kafka Connect to import/export data Step 7: 使用 Kafka Connect 来 导入/导出 数据 Writing data from the console and writing it back to the console is …

ELMo:最好用的词向量(Deep contextualized word representations)论文 pdf

下载地址&#xff1a;https://u20150046.ctfile.com/fs/20150046-376633397 作者&#xff1a;Matthew E. Peters, Mark Neumann, Mohit Iyyer, Matt Gardner, Christopher Clark, Kenton Lee, Luke Zettlemoyer 论文摘要 我们提出一种新的深层语境化的词表示形式&#xff0c…

刚进园子,广州的冬天像夏天

来博客园已经有一段日子了&#xff0c;就好像认识朋友一样&#xff0c;从陌生到熟悉&#xff0c;虽然阅读过的博客文章还不多&#xff0c;但心里还是由然起敬。网络是个信息交流的工具&#xff0c;工具没有好坏之分&#xff0c;都得看用工具的人抱着什么态度。有益处的内容自然…

ava.lang.UnsatisfiedLinkError:

为什么80%的码农都做不了架构师&#xff1f;>>> dalvik.system.PathClassLoader[DexPathList[[zipfile"/data/app/ngdom.android.newphone-1/base.apk"], nativeLibraryDirectories[/data/app/gdom.android.newphone-1/lib/arm, /data/app/gdom.android.n…

分析与设计(AD)简介(3)

这一节主要介绍进行OOAD分析与设计所利用的工具&#xff1a;Visual Studio UML。

序列建模:时间卷积网络取代RNN(An Empirical Evaluation of Generic Convolutional and Recurrent)论文 pdf

下载地址&#xff1a;https://u20150046.ctfile.com/fs/20150046-376633283 作者&#xff1a;Shaojie Bai, J. Zico Kolter, Vladlen Koltun 论文摘要 对于大多数深度学习实践者来说&#xff0c;序列建模与循环网络是同义词。然而&#xff0c;最近的研究结果表明&#xff0c…

Web service到底是什么?

Web service到底是什么&#xff1b;在什么情况下你应该使用Web service。 分布式应用程序和浏览器 研究一下当前的应用程序开发&#xff0c;你会发现一个绝对的倾向&#xff1a;人们开始偏爱基于浏览器的瘦客户应用程序。这当然不是因为瘦客户能够提供更好的用户界面&#xff0…

15级团队学习成果汇报 -- 利用C#语言实现计算器

前几天与15级新加入团队的同学聊天&#xff0c;周日时蓝贵才代表本级汇报了这段时间所学的成果——计算器实例。 我一直对他们说&#xff1a;“要尽快的掌握一些C#语言的基本概念&#xff0c;培养对Coding的感知&#xff0c;后面就可以‘用以致学’&#xff0c;通过‘用’来发…

houdini + maya的pulldownit

houdini maya的pulldownit 制作破碎、3Dtextures等http://www.linecg.com/video/play_27502_28121.html转载于:https://www.cnblogs.com/cainiao001/p/6768831.html

探索机器学习的公平性(Delayed Impact of Fair Machine Learning)论文 pdf

下载地址&#xff1a;https://u20150046.ctfile.com/fs/20150046-376633160 By Lydia T. Liu, Sarah Dean, Esther Rolf, Max Simchowitz, Moritz Hardt (2018) 论文摘要 机器学习中的公平性主要是在静态的分类设置进行研究&#xff0c;而不考虑决策如何随时间改变基础样本总…

数据结构与算法--绪论

本图文涉及的概念&#xff1a; 数据结构&#xff08;Data Structure&#xff09;、数据&#xff08;Data&#xff09;、数据元素&#xff08;Data Element&#xff09;、数据项&#xff08;Data Item&#xff09;、数据逻辑结构&#xff08;Logical Structure&#xff09;、数…

Remove Duplicates from Sorted Array II -- LeetCode

原题链接: http://oj.leetcode.com/problems/remove-duplicates-from-sorted-array-ii/这道题跟Remove Duplicates from Sorted Array比較类似&#xff0c;差别仅仅是这里元素能够反复出现至多两次&#xff0c;而不是一次。事实上也比較简单。仅仅须要维护一个counter。当count…

白领们注意啦:“过劳死”27个危险信号!

要想防止“过劳死”&#xff0c;就必须了解身体为我们发出的“过劳死”信号。日本公众卫生研究所的科研人员曾对日本“过劳死”高发现象做过详细研究&#xff0c;从预防角度&#xff0c;他们列举了27种过劳症状和因素。办公室白领饮食的十大夺命恶习 研究者认为&#xff1a;在…

用于高保真自然图像合成的大规模GAN训练(Large Scale GAN Training For High Fidelity Natural Images)论文 pdf

下载地址&#xff1a;https://u20150046.ctfile.com/fs/20150046-376632643 By Andrew Brock&#xff0c;Jeff Donahue&#xff0c;Karen Simonyan&#xff08;2018&#xff09; 论文摘要 尽管生成图像建模最近取得了进展&#xff0c;但从ImageNet等复杂数据集成功生成高分辨…

建立企业级产品测试报告体系(概述)

基本上每个制造企业都有自己定制得测试报告.这种报告通常是提供给客户作为数据参考,甚至会导入数据库进行调测使用.所以,建立统一的测试报告中心进行数据和报告的管理是有必要的.1.1测试报告体系的愿景 建立统一方便管理, 易于配置式开发的测试报告中心,为客户(包括内部)提供所…

企业IT架构转型是大势所趋

2017年4月26日&#xff0c;南京&#xff0c;我们又来了。 这一次&#xff0c;我们还是一如既往的火爆。但是&#xff0c;这一次我们有点不一样&#xff0c;带来了非常多新鲜的话题。一起来看看有哪些不一样吧。 在“企业级互联网架构-南京峰会”专场&#xff0c;阿里中间件高级…

C#语言与面向对象技术(1)

这段时间有些同学问我&#xff0c;怎么快速的掌握一门计算机语言。 其实&#xff0c;答案很简单&#xff0c;想想自己怎么学习母语的&#xff0c;就是不停的用呗。 对&#xff01;就是不停的用&#xff0c;而且计算机语言更简单&#xff0c;我们的母语还会出现歧义的现象&…

IEEE 解除对华为的限制!

6 月 3 日&#xff0c;IEEE 中文站发布最新中英文双语声明&#xff0c;解除对华为员工参与编辑和审稿的限制。 IEEE 在声明中称&#xff0c;「目前 IEEE已收到相关说明。根据新的信息&#xff0c;华为及其子公司的员工可以参加 IEEE 出版过程的同行评审和编辑工作」。 IEEE 还强…