1. 第一部分:数组
  2. all:布尔全等判断
    const all = (arr, fn = Boolean) => arr.every(fn);

all([4, 2, 3], x => x > 1); // true
all([1, 2, 3]); // true
复制代码2. allEqual:检查数组各项相等
const allEqual = arr => arr.every(val => val === arr[0]);

allEqual([1, 2, 3, 4, 5, 6]); // false
allEqual([1, 1, 1, 1]); // true
复制代码3. approximatelyEqual:约等于
const approximatelyEqual = (v1, v2, epsilon = 0.001) => Math.abs(v1 - v2) < epsilon;

approximatelyEqual(Math.PI / 2.0, 1.5708); // true
复制代码4. arrayToCSV:数组转CSV格式(带空格的字符串)

const arrayToCSV = (arr, delimiter = ',') =>
arr.map(v => v.map(x => "${x}").join(delimiter)).join('\n');

arrayToCSV([['a', 'b'], ['c', 'd']]); // '"a","b"\n"c","d"'
arrayToCSV([['a', 'b'], ['c', 'd']], ';'); // '"a";"b"\n"c";"d"'
复制代码5. arrayToHtmlList:数组转li列表
此代码段将数组的元素转换为

  • 标签,并将其附加到给定ID的列表中。
    const arrayToHtmlList = (arr, listID) =>
    (el => (
    (el = document.querySelector('#' + listID)),
    (el.innerHTML += arr.map(item => <li>${item}</li>).join(''))
    ))();

    arrayToHtmlList(['item 1', 'item 2'], 'myListID');
    复制代码6. average:平均数
    const average = (...nums) => nums.reduce((acc, val) => acc + val, 0) / nums.length;
    average(...[1, 2, 3]); // 2
    average(1, 2, 3); // 2
    复制代码7. averageBy:数组对象属性平均数
    此代码段将获取数组对象属性的平均值
    const averageBy = (arr, fn) =>
    arr.map(typeof fn === 'function' ? fn : val => val[fn]).reduce((acc, val) => acc + val, 0) /
    arr.length;

    averageBy([{ n: 4 }, { n: 2 }, { n: 8 }, { n: 6 }], o => o.n); // 5
    averageBy([{ n: 4 }, { n: 2 }, { n: 8 }, { n: 6 }], 'n'); // 5
    复制代码8. bifurcate:拆分断言后的数组
    可以根据每个元素返回的值,使用reduce()和push() 将元素添加到第二次参数fn中 。
    const bifurcate = (arr, filter) =>
    arr.reduce((acc, val, i) => (acc[filter[i] ? 0 : 1].push(val), acc), [[], []]);
    bifurcate(['beep', 'boop', 'foo', 'bar'], [true, true, false, true]);
    // [ ['beep', 'boop', 'bar'], ['foo'] ]
    复制代码9. castArray:其它类型转数组
    const castArray = val => (Array.isArray(val) ? val : [val]);

    castArray('foo'); // ['foo']
    castArray([1]); // [1]
    castArray(1); // [1]
    复制代码10. compact:去除数组中的无效/无用值
    const compact = arr => arr.filter(Boolean);

    compact([0, 1, false, 2, '', 3, 'a', 'e' * 23, NaN, 's', 34]);
    // [ 1, 2, 3, 'a', 's', 34 ]
    复制代码11. countOccurrences:检测数值出现次数
    const countOccurrences = (arr, val) => arr.reduce((a, v) => (v === val ? a + 1 : a), 0);
    countOccurrences([1, 1, 2, 1, 2, 3], 1); // 3
    复制代码12. deepFlatten:递归扁平化数组
    const deepFlatten = arr => [].concat(...arr.map(v => (Array.isArray(v) ? deepFlatten(v) : v)));

    deepFlatten([1, [2], [[3], 4], 5]); // [1,2,3,4,5]
    复制代码13. difference:寻找差异
    此代码段查找两个数组之间的差异。

    const difference = (a, b) => {
    const s = new Set(b);
    return a.filter(x => !s.has(x));
    };

    difference([1, 2, 3], [1, 2, 4]); // [3]
    复制代码14. differenceBy:先执行再寻找差异
    在将给定函数应用于两个列表的每个元素之后,此方法返回两个数组之间的差异。
    const differenceBy = (a, b, fn) => {
    const s = new Set(b.map(fn));
    return a.filter(x => !s.has(fn(x)));
    };

    differenceBy([2.1, 1.2], [2.3, 3.4], Math.floor); // [1.2]
    differenceBy([{ x: 2 }, { x: 1 }], [{ x: 1 }], v => v.x); // [ { x: 2 } ]
    复制代码15. dropWhile:删除不符合条件的值
    此代码段从数组顶部开始删除元素,直到传递的函数返回为true。
    const dropWhile = (arr, func) => {
    while (arr.length > 0 && !func(arr[0])) arr = arr.slice(1);
    return arr;
    };

    dropWhile([1, 2, 3, 4], n => n >= 3); // [3,4]
    复制代码16. flatten:指定深度扁平化数组
    此代码段第二参数可指定深度。
    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]
    复制代码17. indexOfAll:返回数组中某值的所有索引
    此代码段可用于获取数组中某个值的所有索引,如果此值中未包含该值,则返回一个空数组。
    const indexOfAll = (arr, val) => arr.reduce((acc, el, i) => (el === val ? [...acc, i] : acc), []);

    indexOfAll([1, 2, 3, 1, 2, 3], 1); // [0,3]
    indexOfAll([1, 2, 3], 4); // []
    复制代码18. intersection:两数组的交集

    const intersection = (a, b) => {
    const s = new Set(b);
    return a.filter(x => s.has(x));
    };

    intersection([1, 2, 3], [4, 3, 2]); // [2, 3]
    复制代码19. intersectionWith:两数组都符合条件的交集
    此片段可用于在对两个数组的每个元素执行了函数之后,返回两个数组中存在的元素列表。

    const intersectionBy = (a, b, fn) => {
    const s = new Set(b.map(fn));
    return a.filter(x => s.has(fn(x)));
    };

    intersectionBy([2.1, 1.2], [2.3, 3.4], Math.floor); // [2.1]
    复制代码20. intersectionWith:先比较后返回交集
    const intersectionWith = (a, b, comp) => a.filter(x => b.findIndex(y => comp(x, y)) !== -1);

    intersectionWith([1, 1.2, 1.5, 3, 0], [1.9, 3, 0, 3.9], (a, b) => Math.round(a) === Math.round(b)); // [1.5, 3, 0]
    复制代码21. minN:返回指定长度的升序数组
    const minN = (arr, n = 1) => [...arr].sort((a, b) => a - b).slice(0, n);

    minN([1, 2, 3]); // [1]
    minN([1, 2, 3], 2); // [1,2]
    复制代码22. negate:根据条件反向筛选

    const negate = func => (...args) => !func(...args);

    [1, 2, 3, 4, 5, 6].filter(negate(n => n % 2 === 0)); // [ 1, 3, 5 ]
    复制代码23. randomIntArrayInRange:生成两数之间指定长度的随机数组
    const randomIntArrayInRange = (min, max, n = 1) =>
    Array.from({ length: n }, () => Math.floor(Math.random() * (max - min + 1)) + min);

    randomIntArrayInRange(12, 35, 10); // [ 34, 14, 27, 17, 30, 27, 20, 26, 21, 14 ]
    复制代码24. sample:在指定数组中获取随机数
    const sample = arr => arr[Math.floor(Math.random() * arr.length)];

    sample([3, 7, 9, 11]); // 9
    复制代码25. sampleSize:在指定数组中获取指定长度的随机数
    此代码段可用于从数组中获取指定长度的随机数,直至穷尽数组。
    使用Fisher-Yates算法对数组中的元素进行随机选择。
    const sampleSize = ([...arr], n = 1) => {
    let m = arr.length;
    while (m) {
    const i = Math.floor(Math.random() * m--);
    [arr[m], arr[i]] = [arr[i], arr[m]];
    }
    return arr.slice(0, n);
    };

    sampleSize([1, 2, 3], 2); // [3,1]
    sampleSize([1, 2, 3], 4); // [2,3,1]
    复制代码26. shuffle:“洗牌” 数组
    此代码段使用Fisher-Yates算法随机排序数组的元素。

    const shuffle = ([...arr]) => {
    let m = arr.length;
    while (m) {
    const i = Math.floor(Math.random() * m--);
    [arr[m], arr[i]] = [arr[i], arr[m]];
    }
    return arr;
    };

    const foo = [1, 2, 3];
    shuffle(foo); // [2, 3, 1], foo = [1, 2, 3]
    复制代码27. nest:根据parent_id生成树结构(阿里一面真题)
    根据每项的parent_id,生成具体树形结构的对象。
    const nest = (items, id = null, link = 'parent_id') =>
    items
    .filter(item => item[link] === id)
    .map(item => ({ ...item, children: nest(items, item.id) }));
    复制代码用法:
    const comments = [
    { id: 1, parent_id: null },
    { id: 2, parent_id: 1 },
    { id: 3, parent_id: 1 },
    { id: 4, parent_id: 2 },
    { id: 5, parent_id: 4 }
    ];
    const nestedComments = nest(comments); // [{ id: 1, parent_id: null, children: [...] }]
    复制代码
    强烈建议去理解这个的实现,因为这是我亲身遇到的阿里一面真题:

    1. 第二部分:函数
    2. attempt:捕获函数运行异常
      该代码段执行一个函数,返回结果或捕获的错误对象。
      onst attempt = (fn, ...args) => {
      try {
      return fn(...args);
      } catch (e) {
      return e instanceof Error ? e : new Error(e);
      }
      };
      var elements = attempt(function(selector) {
      return document.querySelectorAll(selector);
      }, '>_>');
      if (elements instanceof Error) elements = []; // elements = []
      复制代码2. defer:推迟执行
      此代码段延迟了函数的执行,直到清除了当前调用堆栈。
      const defer = (fn, ...args) => setTimeout(fn, 1, ...args);

    defer(console.log, 'a'), console.log('b'); // logs 'b' then 'a'
    复制代码3. runPromisesInSeries:运行多个Promises
    const runPromisesInSeries = ps => ps.reduce((p, next) => p.then(next), Promise.resolve());
    const delay = d => new Promise(r => setTimeout(r, d));

    runPromisesInSeries([() => delay(1000), () => delay(2000)]);
    //依次执行每个Promises ,总共需要3秒钟才能完成
    复制代码4. timeTaken:计算函数执行时间

    const timeTaken = callback => {
    console.time('timeTaken');
    const r = callback();
    console.timeEnd('timeTaken');
    return r;
    };

    timeTaken(() => Math.pow(2, 10)); // 1024, (logged): timeTaken: 0.02099609375ms
    复制代码5. createEventHub:简单的发布/订阅模式
    创建一个发布/订阅(发布-订阅)事件集线,有emit,on和off方法。

    使用Object.create(null)创建一个空的hub对象。
    emit,根据event参数解析处理程序数组,然后.forEach()通过传入数据作为参数来运行每个处理程序。
    on,为事件创建一个数组(若不存在则为空数组),然后.push()将处理程序添加到该数组。
    off,用.findIndex()在事件数组中查找处理程序的索引,并使用.splice()删除。

    const createEventHub = () => ({
    hub: Object.create(null),
    emit(event, data) {
    (this.hub[event] || []).forEach(handler => handler(data));
    },
    on(event, handler) {
    if (!this.hub[event]) this.hub[event] = [];
    this.hub[event].push(handler);
    },
    off(event, handler) {
    const i = (this.hub[event] || []).findIndex(h => h === handler);
    if (i > -1) this.hub[event].splice(i, 1);
    if (this.hub[event].length === 0) delete this.hub[event];
    }
    });
    复制代码用法:
    const handler = data => console.log(data);
    const hub = createEventHub();
    let increment = 0;

    // 订阅,监听不同事件
    hub.on('message', handler);
    hub.on('message', () => console.log('Message event fired'));
    hub.on('increment', () => increment++);

    // 发布:发出事件以调用所有订阅给它们的处理程序,并将数据作为参数传递给它们
    hub.emit('message', 'hello world'); // 打印 'hello world' 和 'Message event fired'
    hub.emit('message', { hello: 'world' }); // 打印 对象 和 'Message event fired'
    hub.emit('increment'); // increment = 1

    // 停止订阅
    hub.off('message', handler);
    复制代码6. memoize:缓存函数
    通过实例化一个Map对象来创建一个空的缓存。
    通过检查输入值的函数输出是否已缓存,返回存储一个参数的函数,该参数将被提供给已记忆的函数;如果没有,则存储并返回它。
    const memoize = fn => {
    const cache = new Map();
    const cached = function(val) {
    return cache.has(val) ? cache.get(val) : cache.set(val, fn.call(this, val)) && cache.get(val);
    };
    cached.cache = cache;
    return cached;
    };
    复制代码Ps: 这个版本可能不是很清晰,还有Vue源码版的:
    /**

    • Create a cached version of a pure function.
      */
      export function cached (fn: F): F {
      const cache = Object.create(null)
      return (function cachedFn (str: string) {
      const hit = cache[str]
      return hit || (cache[str] = fn(str))
      }: any)
      }
      复制代码7. once:只调用一次的函数
      const once = fn => {
      let called = false
      return function () {
      if (!called) {
      called = true
      fn.apply(this, arguments)
      }
      }
      };
      复制代码8. flattenObject:以键的路径扁平化对象
      使用递归。

    利用Object.keys(obj)联合Array.prototype.reduce(),以每片叶子节点转换为扁平的路径节点。
    如果键的值是一个对象,则函数使用调用适当的自身prefix以创建路径Object.assign()。
    否则,它将适当的前缀键值对添加到累加器对象。
    prefix除非您希望每个键都有一个前缀,否则应始终省略第二个参数。

    const flattenObject = (obj, prefix = '') =>
    Object.keys(obj).reduce((acc, k) => {
    const pre = prefix.length ? prefix + '.' : '';
    if (typeof obj[k] === 'object') Object.assign(acc, flattenObject(obj[k], pre + k));
    else acc[pre + k] = obj[k];
    return acc;
    }, {});

    flattenObject({ a: { b: { c: 1 } }, d: 1 }); // { 'a.b.c': 1, d: 1 }
    复制代码9. unflattenObject:以键的路径展开对象
    与上面的相反,展开对象。
    const unflattenObject = obj =>
    Object.keys(obj).reduce((acc, k) => {
    if (k.indexOf('.') !== -1) {
    const keys = k.split('.');
    Object.assign(
    acc,
    JSON.parse(
    '{' +
    keys.map((v, i) => (i !== keys.length - 1 ? "${v}":{ : "${v}":)).join('') +
    obj[k] +
    '}'.repeat(keys.length)
    )
    );
    } else acc[k] = obj[k];
    return acc;
    }, {});

    unflattenObject({ 'a.b.c': 1, d: 1 }); // { a: { b: { c: 1 } }, d: 1 }
    复制代码这个的用途,在做Tree组件或复杂表单时取值非常舒服。

    1. 第三部分:字符串
      1.byteSize:返回字符串的字节长度
      const byteSize = str => new Blob([str]).size;

    byteSize('