热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

《JavaScript应用程序设计》一一3.9使用Stamps进行原型继承

本节书摘来华章计算机出版社《JavaScript应用程序设计》一书中的第3章,第3.9节,作者:EricElliott更多章节内容可以访问

本节书摘来华章计算机出版社《Javascript应用程序设计》一书中的第3章,第3.9节,作者:Eric Elliott 更多章节内容可以访问云栖社区“华章计算机”公众号查看。

3.9使用Stamps进行原型继承

对象在Javascript中,有各式各样灵活的特性,相比之下通过Object.create()方法所构建出的对象,感觉就像被“阉割”了一样。开发者总是需要自己去编写额外的代码来实现诸如让享元对象支持数据隐蔽特性这样的特性,所以当你需要将多个对象上的功能做组合时,一般情况下你会感觉非常无力。
现今多数Javascript类库中提供了一套与类继承概念相似的对象复用机制,基于原型继承的Javascript类库占比还是太少,所以就更别提什么业界标准了。既然少量语法糖就能够在Javascript中模拟类的概念,那么为什么不能构建一个囊括了Javascript所有强大特性的原型继承系统呢?
当聊起Javascript中的对象构建时,我们不禁首先会问自己,在Javascript中,对象所具有的最为重要的特性有哪些?
· 原型代理
· 实例状态
· 封装性
Stamps是一个工厂函数,它拥有一组公有属性与方法,这些属性方法用来定义所构建对象的原型代理、默认的实例属性以及为封装私有属性所需的函数。在构建对象方面,Stamps提供了3种不同的继承方式。
· 原型代理:原型继承
· 实例状态:属性混入
· 封装性:闭包式继承
Stampit(https://github.com/dilvie/stampit)是为本书而写的类库,用来向读者展示如何利用Javascript自带的语言特性来简化原型继承,它向外界仅暴露一个入口函数,其函数签名如下:
stampit(methods, state, enclose);
让我们看看如何使用Stampit来构建对象:

var testObj = stampit(
// methods
{delegateMethod: function delegateMethod() {return 'shared property';}
},// state
{instanceProp: 'instance property'
},// enclose
function () {var privateProp = 'private property';this.getPrivate = function getPrivate() {return privateProp;}
}).create();test('Stampit with params', function () {equal(testObj.delegateMethod(), 'shared property','delegate methods should be reachable');ok(Object.getPrototypeOf(testObj).delegateMethod,'delegate methods should be stored on the ' +'delegate prototype');equal(testObj.instanceProp, 'instance property','state should be reachable.');ok(testObj.hasOwnProperty('instanceProp'),'state should be instance safe.');equal(testObj.hasOwnProperty('privateProp'), false,'should hide private properties');equal(testObj.getPrivate(), 'private property','should support privileged methods');
});

在上例中,我们在stampit实例上调用create()方法,它返回testObj的实例。代码下方的测试用例显示,testObj实例已经拥有了诸多原型继承体系下的杀手级特性,你已经无需再花费大量精力去实现自己的工厂函数了。
stampit实例支持方法链式调用,上例中的对象也可以被这样创建:

var testObj = stampit().methods({delegateMethod: function delegateMethod() {return 'shared property';}}).state({instanceProp: 'instance property'}).enclose(function () {var privateProp = 'private property';this.getPrivate = function getPrivate() {return privateProp;}}).create();

由Object.create()构建出的对象会默认使用原型上定义的方法,这些方法被所有实例所共享,这样做在很大程度上节省了内存开销。同理,如果你在程序运行期间修改了原型中的方法,所有实例均会受到波及,如下例:

var stamp = stampit().methods({delegateMethod: function delegateMethod() {return 'shared property';}}),obj1 = stamp(),obj2 = stamp();Object.getPrototypeOf(obj1).delegateMethod = function () {return 'altered';};test('Prototype mutation', function () {equal(obj2.delegateMethod(), 'altered','Instances share the delegate prototype.');
});

state()方法采用了“属性混入”的方式,它将传入的对象视为实例的原型代理,并将该对象的每项属性拷贝后添加至新实例中。由于是拷贝而非直接引用,所以你可以放心大胆地对属性做后期修改。所有stampit实例在做对象构建时,均接受一个可选的字典对象,字典对象中的属性会被混入新实例中,这让对象实例化这一过程变得更为简单。

var person = stampit().state({name: ''}),jimi = person({name: 'Jimi Hendrix'});test('Initialization', function () {equal(jimi.name, 'Jimi Hendrix','Object should be initialized.');});

enclose()方法则采用了“闭包式继承”的方式, 你可以给它传入函数,这些函数彼此独立并且各自拥有闭包作用域,从而可以确保数据的隐蔽性与唯一性。此外,如果在函数中存在多个同名特权方法的定义,那么后一项始终会有限覆盖前一项。在stampit实例中enclose()方法可以被调用多次,所传入的函数会在对象构建时被“激活”。
在初始化一个对象时,有时并不需要将参数一次性全部传入,所以我一直建议将对象的实例化与初始化解耦,引入读写方法(Getter/Setter)可以解决这个问题。

var person = stampit().enclose(function () {var firstName = '',lastName = '';this.getName = function getName() {return firstName + ' ' + lastName;};this.setName = function setName(options) {firstName = options.firstName || '';lastName = options.lastName || '';return this;};
}),jimi = person().setName({firstName: 'Jimi',lastName: 'Hendrix'
});test('Init method', function () {equal(jimi.getName(), 'Jimi Hendrix','Object should be initialized.');});

前面我们介绍了用stampit进行对象构建,其实这仅仅是Javascript面向对象特性的冰山一角。接下来我们介绍的内容你很难在现有的Javascript流行类库中寻觅到,甚至连ES6规范中都没有相关定义。
首先,看如何使用闭包来封装私有数据:

var a = stampit().enclose(function () {var a = 'a';this.getA = function () {return a;};
});a().getA(); // 'a'

stampit实例使用函数作用域来封装私有数据,请注意读写方法(Getter/Setter)需定义在函数内部,才可以访问到闭包中的变量,这一规则同样适用于Javascript中的所有特权函数。
来看另一个例子:

var b = stampit().enclose(function () {var a = 'b';this.getB = function () {return a;};
});b().getB(); // 'b'

这个“拼写错误”是有意为之的,它是为了向你展示实例a与实例b各自封装的同名私有变量不会对彼此的使用造成影响,而且这么做的有趣之处在于:

var c = stampit.compose(a, b),foo = c();foo.getA(); // 'a'
foo.getB(); // 'b'

stampit的静态方法compose()让你能够从多个stampit实例中做原型继承。上述示例演示了使用compose()方法来实现多重继承,我们看到所继承的属性中甚至连私有数据都有囊括,而现今的类继承体系中是做不到这点的。
stampit实例有一个名为fixed的特别属性,它存储着所有methods(实例方法)、state(属性)与enclose(包裹函数)的对象原型。在实例化期间,state对象的所有属性被拷贝至实例中,确保了实例属性操作的安全性;methods对象则作为实例的原型代理来使用,从而使得多个实例间可以共享一套方法;enclose对象将所有函数当作闭包来使用,实现了对私有数据的访问控制。
compose()方法的用途与$.extend()方法很接近,不过它并不是像$.extend()方法那样使用外来对象的属性做扩展,而是借助了stampit实例的fixed属性,compose()方法先是将来自多个stampit实例下的fixed属性做合并,随后向外界返回一个新stampit实例。在对同名属性进行合并时,compose()会优先使用顺序靠后的属性,在这一点上,它与$.extend(), _.extend()等方法的合并策略是一致的。



推荐阅读
  • 目录实现效果:实现环境实现方法一:基本思路主要代码JavaScript代码总结方法二主要代码总结方法三基本思路主要代码JavaScriptHTML总结实 ... [详细]
  • 使用Ubuntu中的Python获取浏览器历史记录原文: ... [详细]
  • 本文介绍了闭包的定义和运转机制,重点解释了闭包如何能够接触外部函数的作用域中的变量。通过词法作用域的查找规则,闭包可以访问外部函数的作用域。同时还提到了闭包的作用和影响。 ... [详细]
  • YOLOv7基于自己的数据集从零构建模型完整训练、推理计算超详细教程
    本文介绍了关于人工智能、神经网络和深度学习的知识点,并提供了YOLOv7基于自己的数据集从零构建模型完整训练、推理计算的详细教程。文章还提到了郑州最低生活保障的话题。对于从事目标检测任务的人来说,YOLO是一个熟悉的模型。文章还提到了yolov4和yolov6的相关内容,以及选择模型的优化思路。 ... [详细]
  • GetWindowLong函数
    今天在看一个代码里头写了GetWindowLong(hwnd,0),我当时就有点费解,靠,上网搜索函数原型说明,死活找不到第 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 云原生边缘计算之KubeEdge简介及功能特点
    本文介绍了云原生边缘计算中的KubeEdge系统,该系统是一个开源系统,用于将容器化应用程序编排功能扩展到Edge的主机。它基于Kubernetes构建,并为网络应用程序提供基础架构支持。同时,KubeEdge具有离线模式、基于Kubernetes的节点、群集、应用程序和设备管理、资源优化等特点。此外,KubeEdge还支持跨平台工作,在私有、公共和混合云中都可以运行。同时,KubeEdge还提供数据管理和数据分析管道引擎的支持。最后,本文还介绍了KubeEdge系统生成证书的方法。 ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • 如何使用Java获取服务器硬件信息和磁盘负载率
    本文介绍了使用Java编程语言获取服务器硬件信息和磁盘负载率的方法。首先在远程服务器上搭建一个支持服务端语言的HTTP服务,并获取服务器的磁盘信息,并将结果输出。然后在本地使用JS编写一个AJAX脚本,远程请求服务端的程序,得到结果并展示给用户。其中还介绍了如何提取硬盘序列号的方法。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • 本文介绍了OC学习笔记中的@property和@synthesize,包括属性的定义和合成的使用方法。通过示例代码详细讲解了@property和@synthesize的作用和用法。 ... [详细]
  • javascript  – 概述在Firefox上无法正常工作
    我试图提出一些自定义大纲,以达到一些Web可访问性建议.但我不能用Firefox制作.这就是它在Chrome上的外观:而那个图标实际上是一个锚点.在Firefox上,它只概述了整个 ... [详细]
  • 如何用UE4制作2D游戏文档——计算篇
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了如何用UE4制作2D游戏文档——计算篇相关的知识,希望对你有一定的参考价值。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 使用在线工具jsonschema2pojo根据json生成java对象
    本文介绍了使用在线工具jsonschema2pojo根据json生成java对象的方法。通过该工具,用户只需将json字符串复制到输入框中,即可自动将其转换成java对象。该工具还能解析列表式的json数据,并将嵌套在内层的对象也解析出来。本文以请求github的api为例,展示了使用该工具的步骤和效果。 ... [详细]
author-avatar
月光魔术师2702935955
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有