最近开始看 underscore.js 源码,并将 underscore.js 源码解读 放在了个人 2016 计划中。javascript
阅读一些著名框架类库的源码,就好像和一个个大师对话,你会学到不少。为何是 underscore?最主要的缘由是 underscore 简短精悍(约 1.5k 行),封装了 100 多个有用的方法,耦合度低,很是适合逐个方法阅读,适合楼主这样的 JavaScript 初学者。从中,你不只能够学到用 void 0 代替 undefined 避免 undefined 被重写等一些小技巧 ,也能够学到变量类型判断、函数节流&函数去抖等经常使用的方法,还能够学到不少浏览器兼容的 hack,更能够学到做者的总体设计思路以及 API 设计的原理(向后兼容)。php
以后楼主会写一系列的文章跟你们分享在源码阅读中学习到的知识。java
underscore-1.8.3 源码解读项目地址 https://github.com/hanzichi/underscore-analysisgit
underscore-1.8.3 源码全文注释 https://github.com/hanzichi/underscore-analysis/blob/master/underscore-1.8.3.js/underscore-1.8.3-analysis.jsgithub
underscore-1.8.3 源码解读系列文章 https://github.com/hanzichi/underscore-analysis/issues数组
欢迎围观~ (若是有兴趣,欢迎 star & watch~)您的关注是楼主继续写做的动力浏览器
很快,Array Functions 部分到了尾声,今天来作个了(xiao)结。框架
underscore 给数组(以及 arguments,这里特别说明下,underscore 的数组扩展方法,一样适用于 arguments)增长了 20 个扩展方法,值得一提的是,不少有意思的方法,好比 map,shuffle 等,都被放在了 Collection Functions 中。本文来看看 Array Functions 中还有哪些有意思的方法(以前没有被说起)。ide
这个方法颇有意思,它的做用是剔除数组中的假值,返回数组副本。函数
实现很是的简单:
_.compact = function(array) { return _.filter(array, _.identity); };
_.filter 咱们在之后会讲到,这里你能够把它理解为 Array.prototype.filter 的一个 polyfill,来看看 _.identity 是个什么东东。
_.identity = function(value) { return value; };
乍一看,_.identity 彷佛没什么卵用,传入一个参数,原封不动返回这个参数,什么鬼?而再看 _.compact 的实现,就会发现很是巧妙!细细品味下,直接过滤了数组的假值,而 _.identity 在源码中能在多个地方复用。
从这个方法能够想到 PHP 的 array_filter 函数。array_filter 的基本用法和 Array.prototype.filter 类似,都是为了过滤数组中的元素。
function isOdd($num) { return $num & 1; } $a = Array(1, 2, 3); $a = array_filter($a, 'isOdd'); var_dump($a); // array // 0 => int 1 // 2 => int 3
可是,值得注意的是:
If no callback is supplied, all entries of array equal to FALSE (see converting to boolean) will be removed.
这就有点 6 了,直接把 _.filter 和 _.compact 两个方法合二为一了。
$a = Array(0, 1, 2, 3, null, false, 4); $a = array_filter($a); var_dump($a); // array // 1 => int 1 // 2 => int 2 // 3 => int 3 // 6 => int 4
Array.prototype.filter 为什么不设计成这样呢?没有 callback 传入的时候,直接过滤假值...
先来看 _.without,它的做用是从数组中剔除指定的元素。
var a = [1, 2, 3, 4, 5]; var ans = _.without(a, 1, 2, 3); console.log(ans); // [4, 5]
恩,没错,剔除数组 a 中的 value 为 1, 2, 3 的元素,这个过程当中用 ===
来进行比较。该方法传入的第一个参数是数组,后面的参数为单个元素。
而 _.difference 呢?和 _.without 的惟一区别是,第二个参数开始传入的是数组。(分别和数组中的元素比较)
var a = [1, 2, 3, 4, 5]; var ans = _.difference(a, [1, 2, 3], [5, 6]); console.log(ans); // [4]
从 a 数组中剔除 1,2,3,5,6。
仔细一想,若是已经实现了 _.difference,咱们把 _.without 的参数放入数组,而后传入 _.difference 就 ok 了!倒过来就不行了(思考下为何)。
来看 _.difference 的实现,很是简单:
// _.difference(array, *others) _.difference = function(array) { // 将 others 数组展开一层 // rest[] 保存展开后的元素组成的数组 // strict 参数为 true // 不能够这样用 _.difference([1, 2, 3, 4, 5], [5, 2], 10); // 10 就会取不到 var rest = flatten(arguments, true, true, 1); // 遍历 array,过滤 return _.filter(array, function(value){ // 若是 value 存在在 rest 中,则过滤掉 return !_.contains(rest, value); }); };
不熟悉 flatten 的能够看看 前文,当 shallow 和 strict 均为 true 时,展开一层,而且过滤非数组元素,便可以起到将多个数组合并的做用。以后利用 ._filter 进行过滤便可。
而 _.without 方法则创建在 _.difference 基础上。
_.without = function(array) { // slice.call(arguments, 1) // 将 arguments 转为数组(同时去掉第一个元素) // 以后即可以调用 _.difference 方法 return _.difference(array, slice.call(arguments, 1)); };
数组的扩展方法就解读到这里了,相关源码能够参考 https://github.com/hanzichi/underscore-analysis/blob/master/underscore-1.8.3.js/src/underscore-1.8.3.js#L450-L693 这部分。接下去要解读的是 Collection Functions 部分,所谓 Collection,正是 Object & Array,也就是说这部分方法既能够用于 Object 也能用于 Array,好比咱们熟悉的 map,filter,shuffle 等等,都在这部份内。
放个预告,下一篇会暂缓下 Collection Functions,讲下 array-like 相关的东西,敬请期待。
PS:坚持一件事真的挺难,一个月来,天天坚持看点源码,几乎把全部业余时间花在了上面,写了 10 篇随笔,每篇文章写的时间不短,关键还须要构思,如何提炼出一个主题,如何写让人看了会有所收获,恩,继续坚持。请关注个人 Repo https://github.com/hanzichi/underscore-analysis 支持我~