微信号:FrontDev

介绍:分享 Web 前端相关的技术文章、工具资源、精选课程、热点资讯

underscore源码解读:Array Functions 相关源码拾遗和小结

2016-06-25 20:09 伯乐专栏\/韩子迟

(点击上方公众号,可快速关注)

作者:伯乐在线专栏作者 - 韩子迟

链接:http://web.jobbole.com/86421/

点击 → 了解如何加入专栏作者

Main


很快,Array Functions 部分到了尾声,今天来做个了(xiao)结。


underscore 给数组(以及 arguments,这里特别说明下,underscore 的数组扩展方法,同样适用于 arguments)增加了 20 个扩展方法,值得一提的是,很多有意思的方法,比如 map,shuffle 等,都被放在了 Collection Functions 中。本文来看看 Array Functions 中还有哪些有意思的方法(之前没有被提及)。


_.compact


这个方法很有意思,它的作用是剔除数组中的假值,返回数组副本。


实现非常的简单:


_.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 传入的时候,直接过滤假值…


_.difference & _.without


先来看 _.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 支持我~

专栏作者简介 ( 点击 → 加入专栏作者 


韩子迟:a JavaScript beginner

打赏支持作者写出更多好文章,谢谢!



【今日微信公号推荐↓】

更多推荐请看值得关注的技术和设计公众号


其中推荐了包括技术设计极客 和 IT相亲相关的热门公众号。技术涵盖:Python、Web前端、Java、安卓、iOS、PHP、C/C++、.NET、Linux、数据库、运维、大数据、算法、IT职场等。点击《值得关注的技术和设计公众号》,发现精彩!


 
前端大全 更多文章 详解Javascript中的Object对象 结合个人经历总结的前端入门方法 前端不为人知的一面–前端冷知识集锦 一份优秀的前端开发工程师简历是怎么样的? 浅谈Web缓存
猜您喜欢 PHP面试经常出的两道题 Java开发必会的Linux命令 移动终端WebAPP开发必备知识 相约在今晚7点,直播PHP5面向对象编程入门第3讲 TesterHome 2015年 分红发放通知