Underscore整体架构浅析
前言
终于,楼主的「Underscore 源码解读系列」underscore-analysis 即将进入尾声,关注下 timeline 会发现楼主最近加快了解读速度。十一月,多事之秋,最近好多事情搞的楼主心力憔悴,身心俱疲,也想尽快把这个系列完结掉,也好了却一件心事。
本文预计是解读系列的倒数第二篇,最后一篇那么显然就是大总结了。楼主的 Underscore 系列解读完整版地址https://github.com/hanzichi/u...
常规调用
之前写的文章,关注点大多在具体的方法,具体的知识细节,也有读者留言建议楼主讲讲整体架构,这是必须会讲的,只是楼主把它安排在了最后,也就是本文,因为楼主觉得不掌握整体架构对于具体方法的理解也是没有大的问题的。
Underscore 大多数时候的调用形式为 _.funcName(xx, xx),这也是 文档中 的调用方式。
- _.each([1, 2, 3], alert);
最简单的实现方式,我们可以把 _ 看做一个简单的对象:
- var _ = {};
- _.each = function() {
- // ...
- };
在 JavaScript 中,一切皆对象,实际上,源码中的 _ 变量是一个方法:
- var _ = function(obj) {
- if (obj instanceof _) return obj;
- if (!(this instanceof _)) return new _(obj);
- this._wrapped = obj;
- };
为什么会是方法?我们接下去看。
OOP
Underscore 支持 OOP 形式的调用:
- _([1, 2, 3]).each(alert);
这其实是非常经典的「无 new 构造」,_ 其实就是一个 构造函数,_([1, 2, 3]) 的结果就是一个对象实例,该实例有个 _wrapped属性,属性值是 [1, 2, 3]。实例要调用 each 方法,其本身没有这个方法,那么应该来自原型链,也就是说 _.prototype 上应该有这个方法,那么,方法是如何挂载上去的呢?
方法挂载
现在我们已经明确以下两点:
- _ 是一个函数(支持无 new 调用的构造函数)
- _ 的属性有很多方法,比如 _.each,_.template 等等
我们的目标是让 _ 的构造实例也能调用这些方法。仔细想想,其实也不难,我们可以遍历 _ 上的属性,如果属性值类型是函数,那么就将函数挂到 _ 的原型链上去。
源码中用来完成这件事的是 _.mixin 方法:
- // Add your own custom functions to the Underscore object.
- // 可向 underscore 函数库扩展自己的方法
- // obj 参数必须是一个对象(JavaScript 中一切皆对象)
- // 且自己的方法定义在 obj 的属性上
- // 如 obj.myFunc = function() {...}
- // 形如 {myFunc: function(){}}
- // 之后便可使用如下: _.myFunc(..) 或者 OOP _(..).myFunc(..)
- _.mixin = function(obj) {
- // 遍历 obj 的 key,将方法挂载到 Underscore 上
- // 其实是将方法浅拷贝到 _.prototype 上
- _.each(_.functions(obj), function(name) {
- // 直接把方法挂载到 _[name] 上
- // 调用类似 _.myFunc([1, 2, 3], ..)
- var func = _[name] = obj[name];
- // 浅拷贝
- // 将 name 方法挂载到 _ 对象的原型链上,使之能 OOP 调用
- _.prototype[name] = function() {
- // 第一个参数
- var args = [this._wrapped];
- // arguments 为 name 方法需要的其他参数
- push.apply(args, arguments);
- // 执行 func 方法
- // 支持链式操作
- return result(this, func.apply(_, args));
- };
- });
- };
- // Add all of the Underscore functions to the wrapper object.
- // 将前面定义的 underscore 方法添加给包装过的对象
- // 即添加到 _.prototype 中
- // 使 underscore 支持面向对象形式的调用
- _.mixin(_);
_.mixin 方法可以向 Underscore 库增加自己定义的方法:
- _.mixin({
- capitalize: function(string) {
- return string.charAt(0).toUpperCase() + string.substring(1).toLowerCase();
- }
- });
- _("fabio").capitalize();
- => "Fabio"
同时,Underscore 也加入了一些 Array 原生的方法:
- // Add all mutator Array functions to the wrapper.
- // 将 Array 原型链上有的方法都添加到 underscore 中
- _.each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
- var method = ArrayProto[name];
- _.prototype[name] = function() {
- var obj = this._wrapped;
- method.apply(obj, arguments);
- if ((name === 'shift' || name === 'splice') && obj.length === 0)
- delete obj[0];
- // 支持链式操作
- return result(this, obj);
- };
- });
- // Add all accessor Array functions to the wrapper.
- // 添加 concat、join、slice 等数组原生方法给 Underscore
- _.each(['concat', 'join', 'slice'], function(name) {
- var method = ArrayProto[name];
- _.prototype[name] = function() {
- return result(this, method.apply(this._wrapped, arguments));
- };
- });
链式调用
Underscore 也支持链式调用:
- // 非 OOP 链式调用
- _.chain([1, 2, 3])
- .map(function(a) {return a * 2;})
- .reverse()
- .value(); // [6, 4, 2]
- // OOP 链式调用
- _([1, 2, 3])
- .chain()
- .map(function(a){return a * 2;})
- .first()
- .value(); // 2
乍一看似乎有 OOP 和非 OOP 两种链式调用形式,其实只是一种,_.chain([1, 2, 3]) 和 _([1, 2, 3]).chain() 的结果是一样的。如何实现的?我们深入 chain 方法看下。
_.chain = function(obj) {
- _.chain = function(obj) {
- // 无论是否 OOP 调用,都会转为 OOP 形式
- // 并且给新的构造对象添加了一个 _chain 属性
- var instance = _(obj);
- // 标记是否使用链式操作
- instance._chain = true;
- // 返回 OOP 对象
- // 可以看到该 instance 对象除了多了个 _chain 属性
- // 其他的和直接 _(obj) 的结果一样
- return instance;
- };
我们看下 _.chain([1, 2, 3]) 的结果,将参数代入函数中,其实就是对参数进行无 new 构造,然后返回实例,只是实例多了个_chain 属性,其他的和直接 _([1, 2, 3]) 一模一样。再来看 _([1, 2, 3]).chain(),_([1, 2, 3]) 返回构造实例,该实例有chain 方法,调用方法,为实例添加 _chain 属性,返回该实例对象。所以,这两者效果是一致的,结果都是转为了 OOP 的形式。
说了这么多,似乎还没讲到正题上,它是如何「链」下去的?我们以如下代码为例:
- _([1, 2, 3])
- .chain()
- .map(function(a){return a * 2;})
- .first()
- .value(); // 2
当调用 map 方法的时候,实际上可能会有返回值。我们看下 _.mixin 源码:
- // 执行 func 方法
- // 支持链式操作
- return result(this, func.apply(_, args));
result 是一个重要的内部帮助函数(Helper function ):
- // Helper function to continue chaining intermediate results.
- // 一个帮助方法(Helper function)
- var result = function(instance, obj) {
- // 如果需要链式操作,则对 obj 运行 chain 方法,使得可以继续后续的链式操作
- // 如果不需要,直接返回 obj
- return instance._chain ? _(obj).chain() : obj;
- };
如果需要链式操作(实例会有带有 _chain 属性),则对运算结果调用 chain 函数,使之可以继续链式调用。
小结
Underscore 整体架构,或者说是基础实现大概就是这个样子,代码部分就讲到这了,接下去系列解读最后一篇,讲讲这段时间(几乎也是历时半年了)的一些心得体会吧,没钱的就捧个人场吧!
作者:韩子迟
来源:51CTO

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
数据中心里的NFV
网络功能虚拟化(NFV)始于服务提供商试图通过专用硬件去解耦网络功能(如路由、防火墙和负载均衡)来实现IT更加简便、灵活并降低成本。随着在标准的Intel x86架构服务器上实现性能改进,NFV作为企业数据中心可行的技术引起了业界广泛的关注。NFV为企业提供了一个高度灵活和弹性的服务交付机制,用于支持端开发周期、API驱动的自动化和弹性的现代应用程序。它使得企业避免为每个网络功能购买物理设备,同时使得企业能够更有效地共享资源,构建横向扩展架构,减少空间和功率需求,并且简化操作。 NFV要求IT团队具备了解虚拟网络功能(VNF)的专业知识,即便是最熟练的IT团队也会面临跨多个虚拟化服务器的补丁管理、服务器维护和配置带来的诸多挑战。新的数据中心NFV策略正在浮现,旨在简化主流企业的NFV技术交付,以便企业降低成本、提供敏捷服务和自动化操作。 最常见的且最成熟的网络服务是由负载均衡提供的L4-L7的服务。由于吞吐量和性能问题,负载均衡服务长期以来一直是与定制化的硬件设备一起交付。然而,使用数据中心NFV技术的软件定义架构相匹配的负载均衡策略,网络服务的交付方式有了重大改进。有以下5个关键属性...
- 下一篇
IoT DDoS警报系统是如何帮助我们预测网络攻击的?
分布式拒绝服务(DDoS)攻击相信大家都不会陌生,当攻击者面对一些安全防护极佳的网站时,拒绝服务往往会成为他们最后的攻击选项。拒绝服务攻击的原理其实非常简单,就是模拟大量的合法用户,向目标服务器发送大量请求,最终导致目标服务器超负荷而瘫痪。现今市面上虽然有许多的抗D服务,但价格却非常的高昂,这对于那些已经存在资金困难的中小型企业来说无疑是一场生与死的博弈。 目前的DDoS攻击通常采用的是放大攻击(PDF)的手段,攻击者的手中往往会掌握大量的‘肉鸡’服务器,并利用类似于‘养鸡场’的客户端控制程序来强制调用‘肉鸡’服务器,向目标服务器发起数量倍增的请求访问。由于目标服务器自身有限的负荷量,最终往往会因此而拒绝服务。想象一下,如果在星期天超级碗的门户因此而瘫痪,他们将会损失多少钱?令人诧异的是,目前在互联网中可以用于此类攻击的互联网协议竟然多达十来种。 将IoT设备添加到列表中 DDoS攻击并不是什么新鲜事物,但如今DDoS攻击的目标却早已不仅仅是个人电脑那么简单。随着物联网的逐渐兴起,DDoS的这只黑手早已伸向了IoT设备。自2014年第一次针对IoT设备攻击以来,近年对于使用ARM,MI...
相关文章
文章评论
共有0条评论来说两句吧...