摘要:今天要讲的是数组展开以及和数组展开息息相关的一个重要的内部方法。也是个布尔值,当为并且也为时,能过滤参数元素中的非数组元素。首先并不需要对数组深度展开,其次传入的是数组,对于非数组元素可以直接忽略。
Why underscore
(觉得这一段眼熟的童鞋可以直接跳到正文了...)
最近开始看 underscore.js 源码,并将 underscore.js 源码解读 放在了我的 2016 计划中。
阅读一些著名框架类库的源码,就好像和一个个大师对话,你会学到很多。为什么是 underscore?最主要的原因是 underscore 简短精悍(约 1.5k 行),封装了 100 多个有用的方法,耦合度低,非常适合逐个方法阅读,适合楼主这样的 JavaScript 初学者。从中,你不仅可以学到用 void 0 代替 undefined 避免 undefined 被重写等一些小技巧 ,也可以学到变量类型判断、函数节流&函数去抖等常用的方法,还可以学到很多浏览器兼容的 hack,更可以学到作者的整体设计思路以及 API 设计的原理(向后兼容)。
之后楼主会写一系列的文章跟大家分享在源码阅读中学习到的知识。
underscore-1.8.3 源码解读项目地址 https://github.com/hanzichi/underscore-analysis
underscore-1.8.3 源码全文注释 https://github.com/hanzichi/underscore-analysis/blob/master/underscore-1.8.3.js/underscore-1.8.3-analysis.js
underscore-1.8.3 源码解读系列文章 https://github.com/hanzichi/underscore-analysis/issues
欢迎围观~ (如果有兴趣,欢迎 star & watch~)您的关注是楼主继续写作的动力
flatten端午休息三天,睡了两天,是该有点产出了。
今天要讲的是数组展开以及和数组展开息息相关的一个重要的内部方法 flatten。
什么是数组展开?简单的说就是将嵌套的数组 "铺平",还是举几个简单的例子吧。
[[[1, 2], [1, 2, 3]], [1, 2]] => [1, 2, 1, 2, 3, 1, 2] [[[1, 2], [1, 2, 3]], [1, 2]] => [[1, 2], [1, 2, 3], 1, 2]
以上两种都是数组展开,第一种我们认为是深度展开,即打破所有嵌套数组,将元素提取出来放入一个数组中;第二种只展开了一层,即只把数组内嵌套的一层数组展开,而没有递归展开下去。
我们首先来看看 flatten 方法的调用形式。
var flatten = function(input, shallow, strict, startIndex) { // ... };
第一个参数 input 即为需要展开的数组,所以 flatten 方法中传入的第一个参数肯定是数组(或者 arguments);第二个参数 shallow 是个布尔值,如果为 false,则表示数组是深度展开,如果为 true 则表示只展开一层;第四个参数表示 input 展开的起始位置,即从 input 数组中第几个元素开始展开。
var ans = flatten([[1, 2], [3, 4]], false, false, 1); console.log(ans); // => [3, 4]
从第 1 项开始展开数组,即忽略了数组的第 0 项([1, 2])。
以上三个参数还是比较容易理解的,相对来说费劲的是第三个参数 strict。strict 也是个布尔值,当 shallow 为 true 并且 strict 也为 true 时,能过滤 input 参数元素中的非数组元素。好难理解啊!我们举个简单的例子。
var ans = flatten([5, 6, [1, 2], [3, 4]], true, true); console.log(ans); // => [1, 2, 3, 4]
5 和 6 是 input 参数中的非数组元素,直接过滤掉了。如果 strict 为 true 并且 shallow 为 false,那么调用 flatten 方法的结果只能是 []。所以我们会看到源码里如果 strict 为 true,那么 shallow 也一定是 true。
直接来看源码,加了非常多的注释。
var flatten = function(input, shallow, strict, startIndex) { // output 数组保存结果 // 即 flatten 方法返回数据 // idx 为 output 的累计数组下标 var output = [], idx = 0; // 根据 startIndex 变量确定需要展开的起始位置 for (var i = startIndex || 0, length = getLength(input); i < length; i++) { var value = input[i]; // 数组 或者 arguments if (isArrayLike(value) && (_.isArray(value) || _.isArguments(value))) { // flatten current level of array or arguments object // (!shallow === true) => (shallow === false) // 则表示需深度展开 // 继续递归展开 if (!shallow) // flatten 方法返回数组 // 将上面定义的 value 重新赋值 value = flatten(value, shallow, strict); // 递归展开到最后一层(没有嵌套的数组了) // 或者 (shallow === true) => 只展开一层 // value 值肯定是一个数组 var j = 0, len = value.length; // 这一步貌似没有必要 // 毕竟 JavaScript 的数组会自动扩充 // 但是这样写,感觉比较好,对于元素的 push 过程有个比较清晰的认识 output.length += len; // 将 value 数组的元素添加到 output 数组中 while (j < len) { output[idx++] = value[j++]; } } else if (!strict) { // (!strict === true) => (strict === false) // 如果是深度展开,即 shallow 参数为 false // 那么当最后 value 不是数组,是基本类型时 // 肯定会走到这个 else-if 判断中 // 而如果此时 strict 为 true,则不能跳到这个分支内部 // 所以 shallow === false 如果和 strict === true 搭配 // 调用 flatten 方法得到的结果永远是空数组 [] output[idx++] = value; } } return output; };
总的来说,就是持续递归调用 flatten,直到不能展开为止。给出 flatten 方法的实现源码位置 https://github.com/hanzichi/underscore-analysis/blob/master/underscore-1.8.3.js/src/underscore-1.8.3.js#L489-L507。
接着我们来看看源码中有用到这个内部方法的 API。
首先是 _.flatten 方法,非常简单,用了 flatten 的前三个参数。
_.flatten = function(array, shallow) { // array => 需要展开的数组 // shallow => 是否只展开一层 // false 为 flatten 方法 strict 变量 return flatten(array, shallow, false); };
前面说了,strict 为 true 只和 shallow 为 true 一起使用,所以没有特殊情况的话 strict 默认为 false。
_.union 方法同样用到了 flatten,这个方法的作用是传入多个数组,然后对数组元素去重。
var ans = _.union([[1]], [1, 2], 3, 4); console.log(ans); // => [[1], 1, 2]
首先并不需要对数组深度展开,其次 _.union 传入的是数组,对于非数组元素可以直接忽略。这两点直接对应了 shallow 参数和 strict 参数均为 true(都不用做容错处理了)。对于一个数组的去重,最后调用 _.unique 即可。
_.union = function() { // 首先用 flatten 方法将传入的数组展开成一个数组 // 然后就可以愉快地调用 _.uniq 方法了 // 假设 _.union([1, 2, 3], [101, 2, 1, 10], [2, 1]); // arguments 为 [[1, 2, 3], [101, 2, 1, 10], [2, 1]] // shallow 参数为 true,展开一层 // 结果为 [1, 2, 3, 101, 2, 1, 10, 2, 1] // 然后对其去重 return _.uniq(flatten(arguments, true, true)); };
而 _.difference,_.pick,_.omit 方法,大家可以自己进源码去看,都大同小异,没什么特别要注意的点。(注意下 startIndex 参数即可)
对于内部方法 flatten,我要总结的是,可能某个内部方法会被多个 API 调用,如何设计地合理,优雅,如何兼顾到各种情况,真的需要强大的实践以及代码能力,这点还需要日后多加摸索。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/86357.html
摘要:原文地址数组展开就是将嵌套的数组扁平化转换为一维的判断每一项是否是数组,然后递归递归调用数组在调用会将数组转换成再执行方法会变成还需要将所有的参数转换为数字,所以这种方式的不足就是如果数组里面是既有数字又有字符串,会全部展开为数字转为数字方 原文地址 数组展开就是将嵌套的数组扁平化(转换为一维的)eg: const arr=[[[1, 2], [1, 2, 3]], [1, 2]] ...
摘要:直接来看例子一目了然,第一个参数是对象,第二个参数可以是一系列的值,也可以是数组数组中含,也可以是迭代函数,我们根据值,或者迭代函数来过滤中的键值对,返回新的对象副本。 Why underscore 最近开始看 underscore.js 源码,并将 underscore.js 源码解读 放在了我的 2016 计划中。 阅读一些著名框架类库的源码,就好像和一个个大师对话,你会学到很多。...
摘要:而这个秒就能理解的代码片段,摒弃了许多不必要的代码,只实现了最核心的部分,不像和那样,考虑参数边界值问题,例如,参数的类型是否符合预期等。使用根据断言函数对数组进行过滤,返回条件为真值的对象。 之前翻译过一篇文章,《我喜欢的5个编程技巧》,里面的一个技巧是借鉴一个网站的代码片段,好奇的小手点下链接后,发现是一个有 47000 多star的仓库,30-seconds-of-code。 仓...
摘要:最近开始看源码,并将源码解读放在了我的计划中。将转为数组同时去掉第一个元素之后便可以调用方法总结数组的扩展方法就解读到这里了,相关源码可以参考这部分。放个预告,下一篇会暂缓下,讲下相关的东西,敬请期待。 Why underscore 最近开始看 underscore.js 源码,并将 underscore.js 源码解读 放在了我的 2016 计划中。 阅读一些著名框架类库的源码,就好...
摘要:专题系列第九篇,讲解如何实现数组的扁平化,并解析的源码扁平化数组的扁平化,就是将一个嵌套多层的数组嵌套可以是任何层数转换为只有一层的数组。 JavaScript 专题系列第九篇,讲解如何实现数组的扁平化,并解析 underscore 的 _.flatten 源码 扁平化 数组的扁平化,就是将一个嵌套多层的数组 array (嵌套可以是任何层数)转换为只有一层的数组。 举个例子,假设有个...
阅读 2715·2021-09-24 09:47
阅读 4365·2021-08-27 13:10
阅读 2980·2019-08-30 15:44
阅读 1281·2019-08-29 12:56
阅读 2593·2019-08-28 18:07
阅读 2612·2019-08-26 14:05
阅读 2549·2019-08-26 13:41
阅读 1265·2019-08-26 13:33