以前翻译过一篇文章,《我喜欢的5个编程技巧》,里面的一个技巧是借鉴一个网站的代码片断,好奇的小手点下连接后,发现是一个有 47000
多star的仓库,30-seconds-of-code。javascript
仓库的名字就让我很惊讶,30秒就能理解一段代码,有点难以想象。看了每一个方法实现的代码都不长,很简短,最复杂的也不过是四、5行代码,的确没有标题党的嫌疑,满满的干活。css
处理的类型还很丰富,有Array、Browser、Date、Function、Math、Node、Object、String、Type、Utility。要了解更多,戳这里。前端
这就是一个工具函数库呀。java
此时让我想到了 lodash.js
和 underscore.js
,用过这两个函数式编程的库,对提供的方法确定不陌生。它们主要是以函数做为主要载体的编程方式,用函数去拆解、抽象的表达式,每一个函数封装特定的功能,做用域局限在函数内部,造成闭包,不对外界产生反作用。react
相信也有不少人阅读过它们的源码,每一个函数很简短,考虑到兼容性,基本都用原生的方式实现,不会调用一些规范中最新推出的方法。若是可以精读它们,对本身的编程能力会有更高的提高,可以掌握不少的技巧。有时你可能只是想快速的了解一个方法大体的实现原理,但要去看源码的话,仍是会有一些门槛。git
而这个30秒就能理解的代码片断,摒弃了许多没必要要的代码,只实现了最核心的部分,不像 lodash.js
和 underscore.js
那样,考虑参数边界值问题,例如,参数的类型是否符合预期等。默认状况下,都是按照传递符合预期的参数处理。github
若是要把这个库用在本身的项目中,没有对参数的判断是很是糟糕的一件事。但不想引 lodash.js
或 underscore.js
这样大的库文件,想本身实现一个简洁的方法快速使用,那么这个库会对你实现本身的方法具备指导意义。不考虑兼容问题的话,你能够直接拷贝这个库的代码片断,加上对参数边界的处理就直接能用。编程
再有一点,这个库之因此简短,可以让你在30秒就理解,主要是能用规范提供的最新方法就用,再也不很费劲的本身实现一套,全都调用了原生提供的方法,包括 ES6
的方法。每一个方法都是独立的,可独立测试,独立运行,和其余的方法互不牵扯,极大的下降了阅读时找各类方法会被打断思路的烦恼。segmentfault
固然,若是你想阅读 lodash.js
或 underscore.js
的源码,先阅读这个库会颇有帮助,它排除了许多没必要要的干扰让你很清晰很明确的get到核心的实现方式。数组
以前也有人翻译过,但都很早,大约2年前了,做者最新最近更新的方法都没有。并且仓库中不止提供 javascript
的方法,还有 css
react
的简短代码,还有其余语言的。基于本身学习的目的,同时也让更多人掌握这些方法的实现方式,决定翻译成中文。
仓库地址:https://github.com/WYseven/30-seconds-of-code。感兴趣的,能够给个 star 哦!
目前已完成数组方法的翻译,点击查看 https://wyseven.github.io/30-seconds-of-code/。其余方法也在持续的更新中。。。
我不建议你闷着头一口气读完,而后头昏眼花的不知道本身看了什么。而是建议你在闲暇之余,工做空隙,断断续续,一天看几个就够了,权当作工做累了休憩时当作消遣来看。
由于篇幅的缘由,如下随机选择了10个方法,你看简单不简单。
chunk
deepFlatten
flatten
initialize2DArray
union
mapObject
pull
reducedFilter
xProd
将数组分块成指定大小的较小数组。
使用 array.from()
建立一个新的数组,该数组的长度就是将要生成的块(chunk)的个数。
使用 array.prototype.slice()
将新数组的每一个元素映射为一个长度为 size
的块(chunk)。
若是原始数组不能被平均分割,那么最后的块(chunk)将包含剩余的元素。
const chunk = (arr, size) => Array.from({ length: Math.ceil(arr.length / size) }, (v, i) => arr.slice(i * size, i * size + size) );
chunk([1, 2, 3, 4, 5], 2); // [[1,2],[3,4],[5]]
深度平铺一个数组。
使用递归。
使用 array. prototype.concat()
和空数组( []
),结合 spread 操做符('...')将数组平铺。
递归平铺数组中的每一个元素。
const deepFlatten = arr => [].concat(...arr.map(v => (Array.isArray(v) ? deepFlatten(v) : v)));
deepFlatten([1, [2], [[3], 4], 5]); // [1,2,3,4,5]
将数组展平到指定的深度。
使用递归,为每一个深度级别 depth
递减 1
。 使用 Array.prototype.reduce()
和 Array.prototype.concat()
来合并元素或数组。 基本状况下,depth
等于 1
中止递归。 省略第二个参数,depth
只能平铺到 1
层(单层平铺) 的深度。
const flatten = (arr, depth = 1) => arr.reduce((a, v) => a.concat(depth > 1 && Array.isArray(v) ? flatten(v, depth - 1) : v), []);
flatten([1, [2], 3, 4]); // [1, 2, 3, 4] flatten([1, [2, [3, [4, 5], 6], 7], 8], 2); // [1, 2, 3, [4, 5], 6, 7, 8]
初始化一个给定行数和列数,以及值的二维数组。
使用 array.prototype.map()
生成 h
行,其中每一行都是长度为 w
的新数组。若是没有提供值 val
,则默认为 null
。
const initialize2DArray = (w, h, val = null) => Array.from({ length: h }).map(() => Array.from({ length: w }).fill(val));
initialize2DArray(2, 2, 0); // [[0,0], [0,0]]
返回两个数组的并集,相同的元素只出现一次。
基于 a
和 b
建立一个 Set
对象,返回转换后的数组。
const union = (a, b) => Array.from(new Set([...a, ...b]));
union([1, 2, 3], [4, 3, 2]); // [1,2,3,4]
使用一个函数将数组的值映射到对象,在键值对中,原始值做为键,映射值做为值。
使用一个匿名的内部函数做用域来声明一个 undefined 的内存空间,使用闭包来存储返回值。 使用一个新的 Array
来存储带有函数映射的数组和一个逗号运算符来返回第二个步骤,而不须要从一个上下文移动到另外一个上下文(因为闭包和操做顺序)。
const mapObject = (arr, fn) => (a => ( (a = [arr, arr.map(fn)]), a[0].reduce((acc, val, ind) => ((acc[val] = a[1][ind]), acc), {}) ))();
const squareIt = arr => mapObject(arr, a => a * a); squareIt([1, 2, 3]); // { 1: 1, 2: 4, 3: 9 }
将指定数量的元素移动到数组的末尾。
两次使用 Array.prototype.slice()
来获取指定索引以后的元素和指定索引以前的元素。
使用展开操做符(...
)将两个数组合成一个数组。
若是 offset
为负数,元素将从结束移动到开始位置。
const offset = (arr, offset) => [...arr.slice(offset), ...arr.slice(0, offset)];
offset([1, 2, 3, 4, 5], 2); // [3, 4, 5, 1, 2] offset([1, 2, 3, 4, 5], -2); // [4, 5, 1, 2, 3]
改变原始数组,过滤掉指定的值。
使用 Array.prototype.filter()
和 array.prototype.include()
过滤指定的值。
使用 Array.prototype.length = 0
经过将数组的长度重置为0来清空数组,并使用 array.prototype.push()
把提取的值从新填充数组。
(对于不改变原始数组的代码片断,请参阅 without
)
const pull = (arr, ...args) => { let argState = Array.isArray(args[0]) ? args[0] : args; let pulled = arr.filter((v, i) => !argState.includes(v)); arr.length = 0; pulled.forEach(v => arr.push(v)); };
let myArray = ['a', 'b', 'c', 'a', 'b', 'c']; pull(myArray, 'a', 'c'); // myArray = [ 'b', 'b' ]
根据条件过滤一个对象数组,同时过滤掉未指定的键。
使用 array.prototype.filter()
根据断言函数 fn
对数组进行过滤,返回条件为真值(truthy)的对象。
在通过过滤后的数组上,使用 array.prototype.map()
和 array.prototype.reduce()
过滤掉在 keys
参数中未提供的键。
const reducedFilter = (data, keys, fn) => data.filter(fn).map(el => keys.reduce((acc, key) => { acc[key] = el[key]; return acc; }, {}) );
const data = [ { id: 1, name: 'john', age: 24 }, { id: 2, name: 'mike', age: 50 } ]; reducedFilter(data, ['id', 'name'], item => item.age > 24); // [{ id: 2, name: 'mike'}]
将两个数组的每一个元素两两进行组合,组合出全部的可能对存在数组中,返回一个存在全部可能性对的数组。
使用 Array.prototype.reduce()
, Array.prototype.map()
和 Array.prototype.concat()
从两个数组的元素中生成全部可能的对,并将它们保存在一个数组中。
const xProd = (a, b) => a.reduce((acc, x) => acc.concat(b.map(y => [x, y])), []);
xProd([1, 2], ['a', 'b']); // [[1, 'a'], [1, 'b'], [2, 'a'], [2, 'b']]
看完后,是否是以为实现特简洁特简单?
看完后,给个 star 哦!仓库地址:https://github.com/WYseven/30...。
若是对你有帮助,请关注【前端技能解锁】: