热门标签 | 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()等方法的合并策略是一致的。



推荐阅读
  • ZSI.generate.Wsdl2PythonError: unsupported local simpleType restriction ... [详细]
  • 这是原文链接:sendingformdata许多情况下,我们使用表单发送数据到服务器。服务器处理数据并返回响应给用户。这看起来很简单,但是 ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • Java序列化对象传给PHP的方法及原理解析
    本文介绍了Java序列化对象传给PHP的方法及原理,包括Java对象传递的方式、序列化的方式、PHP中的序列化用法介绍、Java是否能反序列化PHP的数据、Java序列化的原理以及解决Java序列化中的问题。同时还解释了序列化的概念和作用,以及代码执行序列化所需要的权限。最后指出,序列化会将对象实例的所有字段都进行序列化,使得数据能够被表示为实例的序列化数据,但只有能够解释该格式的代码才能够确定数据的内容。 ... [详细]
  • 目录实现效果:实现环境实现方法一:基本思路主要代码JavaScript代码总结方法二主要代码总结方法三基本思路主要代码JavaScriptHTML总结实 ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • FeatureRequestIsyourfeaturerequestrelatedtoaproblem?Please ... [详细]
  • 成功安装Sabayon Linux在thinkpad X60上的经验分享
    本文分享了作者在国庆期间在thinkpad X60上成功安装Sabayon Linux的经验。通过修改CHOST和执行emerge命令,作者顺利完成了安装过程。Sabayon Linux是一个基于Gentoo Linux的发行版,可以将电脑快速转变为一个功能强大的系统。除了作为一个live DVD使用外,Sabayon Linux还可以被安装在硬盘上,方便用户使用。 ... [详细]
  • 阿里Treebased Deep Match(TDM) 学习笔记及技术发展回顾
    本文介绍了阿里Treebased Deep Match(TDM)的学习笔记,同时回顾了工业界技术发展的几代演进。从基于统计的启发式规则方法到基于内积模型的向量检索方法,再到引入复杂深度学习模型的下一代匹配技术。文章详细解释了基于统计的启发式规则方法和基于内积模型的向量检索方法的原理和应用,并介绍了TDM的背景和优势。最后,文章提到了向量距离和基于向量聚类的索引结构对于加速匹配效率的作用。本文对于理解TDM的学习过程和了解匹配技术的发展具有重要意义。 ... [详细]
  • 本文介绍了闭包的定义和运转机制,重点解释了闭包如何能够接触外部函数的作用域中的变量。通过词法作用域的查找规则,闭包可以访问外部函数的作用域。同时还提到了闭包的作用和影响。 ... [详细]
  • t-io 2.0.0发布-法网天眼第一版的回顾和更新说明
    本文回顾了t-io 1.x版本的工程结构和性能数据,并介绍了t-io在码云上的成绩和用户反馈。同时,还提到了@openSeLi同学发布的t-io 30W长连接并发压力测试报告。最后,详细介绍了t-io 2.0.0版本的更新内容,包括更简洁的使用方式和内置的httpsession功能。 ... [详细]
  • 推荐系统遇上深度学习(十七)详解推荐系统中的常用评测指标
    原创:石晓文小小挖掘机2018-06-18笔者是一个痴迷于挖掘数据中的价值的学习人,希望在平日的工作学习中,挖掘数据的价值, ... [详细]
  • 怀疑是每次都在新建文件,具体代码如下 ... [详细]
  • Tomcat/Jetty为何选择扩展线程池而不是使用JDK原生线程池?
    本文探讨了Tomcat和Jetty选择扩展线程池而不是使用JDK原生线程池的原因。通过比较IO密集型任务和CPU密集型任务的特点,解释了为何Tomcat和Jetty需要扩展线程池来提高并发度和任务处理速度。同时,介绍了JDK原生线程池的工作流程。 ... [详细]
  • 不同优化算法的比较分析及实验验证
    本文介绍了神经网络优化中常用的优化方法,包括学习率调整和梯度估计修正,并通过实验验证了不同优化算法的效果。实验结果表明,Adam算法在综合考虑学习率调整和梯度估计修正方面表现较好。该研究对于优化神经网络的训练过程具有指导意义。 ... [详细]
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社区 版权所有