{"action":"created","issue":{"url":"https://api.github.com/repos/hanzichi/underscore-analysis/issues/12","repository_url":"https://api.github.com/repos/hanzichi/underscore-analysis","labels_url":"https://api.github.com/repos/hanzichi/underscore-analysis/issues/12/labels{/name}","comments_url":"https://api.github.com/repos/hanzichi/underscore-analysis/issues/12/comments","events_url":"https://api.github.com/repos/hanzichi/underscore-analysis/issues/12/events","html_url":"https://github.com/hanzichi/underscore-analysis/issues/12","id":159850302,"number":12,"title":"Array Functions 相关源码拾遗 & 小结","user":{"login":"hanzichi","id":10890665,"avatar_url":"https://avatars.githubusercontent.com/u/10890665?v=3","gravatar_id":"","url":"https://api.github.com/users/hanzichi","html_url":"https://github.com/hanzichi","followers_url":"https://api.github.com/users/hanzichi/followers","following_url":"https://api.github.com/users/hanzichi/following{/other_user}","gists_url":"https://api.github.com/users/hanzichi/gists{/gist_id}","starred_url":"https://api.github.com/users/hanzichi/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/hanzichi/subscriptions","organizations_url":"https://api.github.com/users/hanzichi/orgs","repos_url":"https://api.github.com/users/hanzichi/repos","events_url":"https://api.github.com/users/hanzichi/events{/privacy}","received_events_url":"https://api.github.com/users/hanzichi/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/hanzichi/underscore-analysis/labels/underscore.js","name":"underscore.js","color":"fbca04"}],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":2,"created_at":"2016-06-12T22:00:04Z","updated_at":"2016-06-14T09:29:24Z","closed_at":null,"body":"# Why underscore\r\n\r\n最近开始看 underscore.js 源码，并将 [underscore.js 源码解读](https://github.com/hanzichi/underscore-analysis) 放在了我的 2016 计划中。\r\n\r\n阅读一些著名框架类库的源码，就好像和一个个大师对话，你会学到很多。为什么是 underscore？最主要的原因是 underscore 简短精悍（约 1.5k 行），封装了 100 多个有用的方法，耦合度低，非常适合逐个方法阅读，适合楼主这样的 JavaScript 初学者。从中，你不仅可以学到用 void 0 代替 undefined 避免 undefined 被重写等一些小技巧 ，也可以学到变量类型判断、函数节流&函数去抖等常用的方法，还可以学到很多浏览器兼容的 hack，更可以学到作者的整体设计思路以及 API 设计的原理（向后兼容）。\r\n\r\n之后楼主会写一系列的文章跟大家分享在源码阅读中学习到的知识。\r\n\r\n- underscore-1.8.3 源码解读项目地址 <https://github.com/hanzichi/underscore-analysis>\r\n- underscore-1.8.3 源码全文注释 <https://github.com/hanzichi/underscore-analysis/blob/master/underscore-1.8.3.js/underscore-1.8.3-analysis.js>\r\n- underscore-1.8.3 源码解读系列文章 <https://github.com/hanzichi/underscore-analysis/issues>\r\n\r\n\r\n欢迎围观~ （**如果有兴趣，欢迎 star & watch~**）您的关注是楼主继续写作的动力\r\n\r\n\r\n# Main\r\n\r\n很快，Array Functions 部分到了尾声，今天来做个了（xiao）结。\r\n\r\nunderscore 给数组（以及 arguments，这里特别说明下，underscore 的数组扩展方法，同样适用于 arguments）增加了 20 个扩展方法，值得一提的是，很多有意思的方法，比如 map，shuffle 等，都被放在了 Collection Functions 中。本文来看看 Array Functions 中还有哪些有意思的方法（之前没有被提及）。\r\n\r\n### _.compact\r\n\r\n这个方法很有意思，它的作用是剔除数组中的假值，返回数组副本。\r\n\r\n实现非常的简单：\r\n\r\n```javascript\r\n_.compact = function(array) {\r\n  return _.filter(array, _.identity);\r\n};\r\n```\r\n\r\n\\_.filter 我们在以后会讲到，这里你可以把它理解为 Array.prototype.filter 的一个 polyfill，来看看 \\_.identity 是个什么东东。\r\n\r\n```javascript\r\n_.identity = function(value) {\r\n  return value;\r\n};\r\n```\r\n\r\n乍一看，\\_.identity 似乎没什么卵用，传入一个参数，原封不动返回这个参数，什么鬼？而再看 \\_.compact 的实现，就会发现非常巧妙！细细品味下，直接过滤了数组的假值，而 \\_.identity 在源码中能在多个地方复用。\r\n\r\n从这个方法可以想到 PHP 的 array\\_filter 函数。array\\_filter 的基本用法和 Array.prototype.filter 相似，都是为了过滤数组中的元素。\r\n\r\n```php\r\nfunction isOdd($num) {\r\n  return $num & 1;\r\n}\r\n\r\n$a = Array(1, 2, 3);\r\n\r\n$a = array_filter($a, 'isOdd');\r\n\r\nvar_dump($a);\r\n\r\n// array\r\n//   0 => int 1\r\n//   2 => int 3\r\n```\r\n\r\n但是，值得注意的是：\r\n\r\n> If no callback is supplied, all entries of array equal to FALSE (see converting to boolean) will be removed.\r\n\r\n这就有点 6 了，直接把 \\_.filter 和 \\_.compact 两个方法合二为一了。\r\n\r\n```php\r\n$a = Array(0, 1, 2, 3, null, false, 4);\r\n\r\n$a = array_filter($a);\r\n\r\nvar_dump($a);\r\n\r\n// array\r\n//   1 => int 1\r\n//   2 => int 2\r\n//   3 => int 3\r\n//   6 => int 4\r\n```\r\n\r\nArray.prototype.filter 为何不设计成这样呢？没有 callback 传入的时候，直接过滤假值...\r\n\r\n\r\n### \\_.difference & \\_.without\r\n\r\n先来看 \\_.without，它的作用是从数组中剔除指定的元素。\r\n\r\n```javascript\r\nvar a = [1, 2, 3, 4, 5];\r\nvar ans = _.without(a, 1, 2, 3);\r\nconsole.log(ans); // [4, 5]\r\n```\r\n\r\n恩，没错，剔除数组 a 中的 value 为 1, 2, 3 的元素，这个过程中用 `===` 来进行比较。该方法传入的第一个参数是数组，后面的参数为单个元素。\r\n\r\n而 \\_.difference 呢？和 \\_.without 的唯一区别是，第二个参数开始传入的是数组。（分别和数组中的元素比较）\r\n\r\n```javascript\r\nvar a = [1, 2, 3, 4, 5];\r\nvar ans = _.difference(a, [1, 2, 3], [5, 6]);\r\nconsole.log(ans); // [4]\r\n```\r\n\r\n从 a 数组中剔除 1，2，3，5，6。\r\n\r\n仔细一想，如果已经实现了 \\_.difference，我们把 \\_.without 的参数放入数组，然后传入 \\_.difference 就 ok 了！倒过来就不行了（思考下为什么）。\r\n\r\n来看 \\_.difference 的实现，非常简单：\r\n\r\n```javascript\r\n// _.difference(array, *others)\r\n_.difference = function(array) {\r\n  // 将 others 数组展开一层\r\n  // rest[] 保存展开后的元素组成的数组\r\n  // strict 参数为 true\r\n  // 不可以这样用 _.difference([1, 2, 3, 4, 5], [5, 2], 10);\r\n  // 10 就会取不到\r\n  var rest = flatten(arguments, true, true, 1);\r\n  \r\n  // 遍历 array，过滤\r\n  return _.filter(array, function(value){\r\n    // 如果 value 存在在 rest 中，则过滤掉\r\n    return !_.contains(rest, value);\r\n  });\r\n};\r\n```\r\n\r\n不熟悉 flatten 的可以看看 [前文](https://github.com/hanzichi/underscore-analysis/issues/10)，当 shallow 和 strict 均为 true 时，展开一层，并且过滤非数组元素，即可以起到将多个数组合并的作用。之后利用 \\._filter 进行过滤即可。\r\n\r\n而 \\_.without 方法则建立在 \\_.difference 基础上。\r\n\r\n```javascript\r\n_.without = function(array) {\r\n  // slice.call(arguments, 1)\r\n  // 将 arguments 转为数组（同时去掉第一个元素）\r\n  // 之后便可以调用 _.difference 方法\r\n  return _.difference(array, slice.call(arguments, 1));\r\n};\r\n```\r\n\r\n\r\n# 总结\r\n\r\n数组的扩展方法就解读到这里了，相关源码可以参考 <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 等等，都在这部分内。\r\n\r\n放个预告，下一篇会暂缓下 Collection Functions，讲下 array-like 相关的东西，敬请期待。\r\n\r\nPS：坚持一件事真的挺难，一个月来，每天坚持看点源码，几乎把所有业余时间花在了上面，写了 10 篇随笔，每篇文章写的时间不短，关键还需要构思，如何提炼出一个主题，如何写让人看了会有所收获，恩，继续坚持。请关注我的 Repo <https://github.com/hanzichi/underscore-analysis> 支持我~\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n  \r\n"},"comment":{"url":"https://api.github.com/repos/hanzichi/underscore-analysis/issues/comments/225828142","html_url":"https://github.com/hanzichi/underscore-analysis/issues/12#issuecomment-225828142","issue_url":"https://api.github.com/repos/hanzichi/underscore-analysis/issues/12","id":225828142,"user":{"login":"hanzichi","id":10890665,"avatar_url":"https://avatars.githubusercontent.com/u/10890665?v=3","gravatar_id":"","url":"https://api.github.com/users/hanzichi","html_url":"https://github.com/hanzichi","followers_url":"https://api.github.com/users/hanzichi/followers","following_url":"https://api.github.com/users/hanzichi/following{/other_user}","gists_url":"https://api.github.com/users/hanzichi/gists{/gist_id}","starred_url":"https://api.github.com/users/hanzichi/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/hanzichi/subscriptions","organizations_url":"https://api.github.com/users/hanzichi/orgs","repos_url":"https://api.github.com/users/hanzichi/repos","events_url":"https://api.github.com/users/hanzichi/events{/privacy}","received_events_url":"https://api.github.com/users/hanzichi/received_events","type":"User","site_admin":false},"created_at":"2016-06-14T09:29:24Z","updated_at":"2016-06-14T09:29:24Z","body":"    _.without.apply(null, args);\r\n\r\n这步的结果还是 args 吧？不知道我有没有理解错 @WangBoxue "}}
{"actor":{"display_login":"hanzichi"}}