Underscore 整体架构浅析
前言
终于,楼主的「Underscore 源码解读系列」underscore-analysis 即将进入尾声,关注下 timeline 会发现楼主最近加快了解读速度。十一月,多事之秋,最近好多事情搞的楼主心力憔悴,身心俱疲,也想尽快把这个系列完结掉,也好了却一件心事。
本文预计是解读系列的倒数第二篇,最后一篇那么显然就是大总结了。楼主的 Underscore 系列解读完整版地址 https://github.com/hanzichi/underscore-analysis
常规调用
之前写的文章,关注点大多在具体的方法,具体的知识细节,也有读者留言建议楼主讲讲整体架构,这是必须会讲的,只是楼主把它安排在了最后,也就是本文,因为楼主觉得不掌握整体架构对于具体方法的理解也是没有大的问题的。
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) {
// 无论是否 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 整体架构,或者说是基础实现大概就是这个样子,代码部分就讲到这了,接下去系列解读最后一篇,讲讲这段时间(几乎也是历时半年了)的一些心得体会吧,没钱的就捧个人场吧!
Underscore 整体架构浅析的更多相关文章
- [数据库系列之MySQL] Mysql整体架构浅析一
一.引言 平时我们在做Java系统时,一般情况下都会连接到一个MySQL数据库上去,执行各种增删改查的语句.大部分的Java工程师对MySQL的了解和掌握程度,大致就停留在这么一个阶段:对MySQL可 ...
- 【深入浅出jQuery】源码浅析--整体架构
最近一直在研读 jQuery 源码,初看源码一头雾水毫无头绪,真正静下心来细看写的真是精妙,让你感叹代码之美. 其结构明晰,高内聚.低耦合,兼具优秀的性能与便利的扩展性,在浏览器的兼容性(功能缺陷.渐 ...
- 【深入浅出jQuery】源码浅析--整体架构(转)
最近一直在研读 jQuery 源码,初看源码一头雾水毫无头绪,真正静下心来细看写的真是精妙,让你感叹代码之美. 其结构明晰,高内聚.低耦合,兼具优秀的性能与便利的扩展性,在浏览器的兼容性(功能缺陷.渐 ...
- 浅析MyBatis(一):由一个快速案例剖析MyBatis的整体架构与运行流程
MyBatis 是轻量级的 Java 持久层中间件,完全基于 JDBC 实现持久化的数据访问,支持以 xml 和注解的形式进行配置,能灵活.简单地进行 SQL 映射,也提供了比 JDBC 更丰富的结果 ...
- jQuery 2.0.3 源码分析core - 整体架构
拜读一个开源框架,最想学到的就是设计的思想和实现的技巧. 废话不多说,jquery这么多年了分析都写烂了,老早以前就拜读过, 不过这几年都是做移动端,一直御用zepto, 最近抽出点时间把jquery ...
- [转]Android App整体架构设计的思考
1. 架构设计的目的 对程序进行架构设计的原因,归根到底是为了提高生产力.通过设计使程序模块化,做到模块内部的高聚合和模块之间的低耦合.这样做的好处是使得程序在开发的过程中,开发人员只需要专注于一点, ...
- jQuery整体架构源码解析(转载)
jQuery整体架构源码解析 最近一直在研读 jQuery 源码,初看源码一头雾水毫无头绪,真正静下心来细看写的真是精妙,让你感叹代码之美. 其结构明晰,高内聚.低耦合,兼具优秀的性能与便利的扩展性, ...
- 《深入理解bootstrap》读书笔记:第二章 整体架构
一. 整体架构 1. CSS-12栅格系统 把网页宽度均分为12等分(保留15位精度)--这是bootstrap的核心功能. 2.基础布局组件 包括排版.按钮.表格.布局.表单等等. 3.jQu ...
- Nginx的负载均衡 - 整体架构
Nginx的负载均衡 - 整体架构 Nginx版本:1.9.1 我的博客:http://blog.csdn.net/zhangskd Nginx目前提供的负载均衡模块: ngx_http_upstre ...
随机推荐
- 介介介是一个ORM
介个是一个ORM,介个ORM基于Dapper扩展. 为什么需要一个ORM呢? 支持简单的LINQ查询 但是不能连表查询,why?why?why?为什么不能连接查询 ^.^ ok.但是就是不支持.哈哈哈 ...
- [占位-未完成]scikit-learn一般实例之十二:用于RBF核的显式特征映射逼近
It shows how to use RBFSampler and Nystroem to approximate the feature map of an RBF kernel for clas ...
- request 对象和 response 对象
Web服务器收到客户端的http请求,会针对每一次请求,分别创建一个用于代表请求的request对象.和代表响应的response对象 HttpServletResponse HttpServletR ...
- iis6.0与asp.net的运行原理
这几天上网翻阅了不少前辈们的关于iis和asp.net运行原理的博客,学的有点零零散散,花了好长时间做了一个小结(虽然文字不多,但也花了不少时间呢),鄙人不才,难免有理解不道的地方,还望前辈们不吝赐教 ...
- apache中怎么配置网站的默认首页
配置方法如下:1.首先需要打开Apache的配置文件httpd.conf文件,使用一般的编辑器或者记事本打开均可.2.找到或者搜索到如下字段:<IfModule dir_module> D ...
- Lind.DDD敏捷领域驱动框架~介绍
回到占占推荐博客索引 最近觉得自己的框架过于复杂,在实现开发使用中有些不爽,自己的朋友们也经常和我说,框架太麻烦了,要引用的类库太多:之前架构之所以这样设计,完全出于对职责分离和代码附复用的考虑,主要 ...
- 运算符.png
- HTTP 错误 404.3 - Not Found 由于扩展配置问题而无法提供您请求的页面。如果该页面是脚本 ,请添加处理程序。如果下载文件,请添加 MIME 映射。 IIS站点中添加WCF项目后浏览网站报错解决方法。
vs2013 wcf服务 windows10 家庭中文版 上图中的红色没有出现就按照下面的方法做: 按照上图所示勾选. 刷新上图中你的网站,查看上图右边的内容是否出现,如果出现,再次重新浏览网站,看一 ...
- 案例借鉴 | 某通讯巨头的IT建设方案
成都联通作为合并重组后的中国联通在成都的分支机构,拥有基础扎实的通信网络和当前最先进技术的WCDMA网络.随着3G和4G业务的发展领先,成都联通凭借其出色的网络能力和服务,在用户中赢得了口碑. 在IT ...
- CRM 2013 相关下载 / 2013-10-11
CRM 2013的安装文件,软件开发工具包(Sdk)以及实施指南,在微软官方网站已经有下载了. 具体地址如下: Name Url 发布日期 语言版本 说明 CRM Server htt ...