满足所有开发需求的简短 JavaScript 代码片段

liuxiaobai
2022-03-08 / 0 评论 / 5 阅读 / 正在检测是否收录...

CSV 转数组

将逗号分隔值 (CSV) 字符串转换为二维数组。

  • Array.prototype.indexOf()查找第一个换行符 ( \n)。
  • 如果 omitFirstRow 为真,则使用 Array.prototype.slice() 删除第一行(标题行)。
  • 使用 String.prototype.split() 为每一行创建一个字符串。
  • 使用 String.prototype.split() 使用提供的分隔符分隔每行中的值。
  • 省略第二个参数 ,delimiter以使用默认定界符','
  • 省略第三个参数,omitFirstRow以包含 CSV 字符串的第一行(标题行)。
const CSVToArray = (data, delimiter = ',', omitFirstRow = false) =>
  data
    .slice(omitFirstRow ? data.indexOf('\n') + 1 : 0)
    .split('\n')
    .map(v => v.split(delimiter));
CSVToArray('a,b\nc,d'); // [['a', 'b'], ['c', 'd']];
CSVToArray('a;b\nc;d', ';'); // [['a', 'b'], ['c', 'd']];
CSVToArray('col1,col2\na,b\nc,d', ',', true); // [['a', 'b'], ['c', 'd']];

CSV 转 JSON

将逗号分隔值 (CSV) 字符串转换为二维对象数组。字符串的第一行用作标题行。

  • 用于Array.prototype.indexOf()查找第一个换行符 ( \n)。
  • 用于Array.prototype.slice()删除第一行(标题行)并将String.prototype.split()其分成值,使用提供的delimiter.
  • 用于String.prototype.split()为每一行创建一个字符串。
  • 用于String.prototype.split()分隔每行中的值,使用提供的delimiter.
  • 用于Array.prototype.reduce()为每一行的值创建一个对象,其中的键是从标题行中解析出来的。
  • 省略第二个参数 ,delimiter以使用默认定界符,
const CSVToJSON = (data, delimiter = ',') => {
  const titles = data.slice(0, data.indexOf('\n')).split(delimiter);
  return data
    .slice(data.indexOf('\n') + 1)
    .split('\n')
    .map(v => {
      const values = v.split(delimiter);
      return titles.reduce(
        (obj, title, index) => ((obj[title] = values[index]), obj),
        {}
      );
    });
};
CSVToJSON('col1,col2\na,b\nc,d');
// [{'col1': 'a', 'col2': 'b'}, {'col1': 'c', 'col2': 'd'}];
CSVToJSON('col1;col2\na;b\nc;d', ';');
// [{'col1': 'a', 'col2': 'b'}, {'col1': 'c', 'col2': 'd'}];

HSB转RGB 将 HSB 颜色元组转换为 RGB 格式。

  • 使用HSB 到 RGB 转换公式转换为适当的格式。
  • 输入参数的范围为H:[0, 360],S:[0, 100],B:[0, 100]。
  • 所有输出值的范围是[0, 255]。
const HSBToRGB = (h, s, b) => {
  s /= 100;
  b /= 100;
  const k = (n) => (n + h / 60) % 6;
  const f = (n) => b * (1 - s * Math.max(0, Math.min(k(n), 4 - k(n), 1)));
  return [255 * f(5), 255 * f(3), 255 * f(1)];
};
HSBToRGB(18, 81, 99); // [252.45, 109.31084999999996, 47.965499999999984]

HSL转RGB

将HSL颜色元组转换为RGB格式。

  • 使用HSL到RGB转换公式转换为适当的格式。
  • 输入参数的范围是H:[0,360],S:[0,100],L:[0,100]。
  • 所有输出值的范围为[0, 255]。
const HSLToRGB = (h, s, l) => {
  s /= 100;
  l /= 100;
  const k = n => (n + h / 30) % 12;
  const a = s * Math.min(l, 1 - l);
  const f = n =>
    l - a * Math.max(-1, Math.min(k(n) - 3, Math.min(9 - k(n), 1)));
  return [255 * f(0), 255 * f(8), 255 * f(4)];
};
HSLToRGB(13, 100, 11); // [56.1, 12.155, 0]

JSON写入到文件

将JSON对象写入文件。

  • 使用fs.writeFileSync()模板文字和JSON.stringify()json对象写入.json文件。
const fs = require('fs');

const JSONToFile = (obj, filename) =>
  fs.writeFileSync(`${filename}.json`, JSON.stringify(obj, null, 2));
JSONToFile({ test: 'is passed' }, 'testJsonFile');
// 将对象写入 'testJsonFile.json'

JSON转CSV

将对象数组转换为逗号分隔值(CSV)字符串,该字符串仅包含指定的columns

  • 使用Array.prototype.join()组合columns中的所有名称,使用提供的delimiter创建第一行。
  • 使用Array.prototype.map()Array.prototype.reduce()为每个对象创建一个行。用空字符串替换不存在的值,并且仅在columns中映射值。
  • 使用Array.prototype.join()将所有行组合成一个字符串,用换行符(\n)分隔每行。
  • 省略第三个参数,delimiter,以使用默认分隔符','”。
const JSONtoCSV = (arr, columns, delimiter = ',') =>
  [
    columns.join(delimiter),
    ...arr.map(obj =>
      columns.reduce(
        (acc, key) =>
          `${acc}${!acc.length ? '' : delimiter}"${!obj[key] ? '' : obj[key]}"`,
        ''
      )
    ),
  ].join('\n');
JSONtoCSV(
  [{ a: 1, b: 2 }, { a: 3, b: 4, c: 5 }, { a: 6 }, { b: 7 }],
  ['a', 'b']
); // 'a,b\n"1","2"\n"3","4"\n"6",""\n"","7"'
JSONtoCSV(
  [{ a: 1, b: 2 }, { a: 3, b: 4, c: 5 }, { a: 6 }, { b: 7 }],
  ['a', 'b'],
  ';'
); // 'a;b\n"1";"2"\n"3";"4"\n"6";""\n"";"7"'

RGB转HSB

将RGB颜色元组转换为HSB格式。

  • 使用RGB到HSB转换公式转换为适当的格式。
  • 所有输入参数的范围是[0, 255]。
  • 结果值的范围是H:[0,360],S:[0,100],B:[0,100]。
const RGBToHSB = (r, g, b) => {
  r /= 255;
  g /= 255;
  b /= 255;
  const v = Math.max(r, g, b),
    n = v - Math.min(r, g, b);
  const h =
    n === 0 ? 0 : n && v === r ? (g - b) / n : v === g ? 2 + (b - r) / n : 4 + (r - g) / n;
  return [60 * (h < 0 ? h + 6 : h), v && (n / v) * 100, v * 100];
};
RGBToHSB(252, 111, 48);
// [18.529411764705856, 80.95238095238095, 98.82352941176471]

RGB转HSL

将RGB颜色元组转换为HSL格式。

  • 使用RGB到HSL转换公式转换为适当的格式。
  • 所有输入参数的范围是[0, 255]。
  • 结果值的范围是H:[0,360],S:[0,100],L:[0,100]。
const RGBToHSL = (r, g, b) => {
  r /= 255;
  g /= 255;
  b /= 255;
  const l = Math.max(r, g, b);
  const s = l - Math.min(r, g, b);
  const h = s
    ? l === r
      ? (g - b) / s
      : l === g
      ? 2 + (b - r) / s
      : 4 + (r - g) / s
    : 0;
  return [
    60 * h < 0 ? 60 * h + 360 : 60 * h,
    100 * (s ? (l <= 0.5 ? s / (2 * l - s) : s / (2 - (2 * l - s))) : 0),
    (100 * (2 * l - s)) / 2,
  ];
};
RGBToHSL(45, 23, 11); // [21.17647, 60.71428, 10.98039]

RGB转十六进制

将RGB组件的值转换为十六进制颜色代码。

  • 使用按位左移运算符(<<)和Number.prototype.toString()将给定的RGB参数转换为十六进制字符串。
  • 使用String.prototype.padStart()获取6位十六进制值。
const RGBToHex = (r, g, b) =>
  ((r << 16) + (g << 8) + b).toString(16).padStart(6, '0');
RGBToHex(255, 165, 1); // 'ffa501'

拼接URL

将所有给定的URL段连接在一起,然后将生成的URL规范化。

  • 使用Array.prototype.join()组合URL段。
  • 使用一系列带有各种正则表达式的String.prototype.replace()调用来规范化生成的URL(删除双斜杠,为协议添加适当的斜杠,在参数之前删除斜杠,将参数与'&'组合,并规范化第一个参数分隔符)。
const URLJoin = (...args) =>
  args
    .join('/')
    .replace(/[\/]+/g, '/')
    .replace(/^(.+):\//, '$1://')
    .replace(/^file:/, 'file:/')
    .replace(/\/(\?|&|#[^!])/g, '$1')
    .replace(/\?/g, '&')
    .replace('&', '?');
URLJoin('http://www.google.com', 'a', '/b/cd', '?foo=123', '?bar=foo');
// 'http://www.google.com/a/b/cd?foo=123&bar=foo'

生成UUID(浏览器)

在浏览器中生成UUID。

  • 使用Crypto.getRandomValues()生成符合RFC4122版本4的UUID。
  • 使用Number.prototype.toString()将其转换为适当的UUID(十六进制字符串)。
const UUIDGeneratorBrowser = () =>
  ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, c =>
    (
      c ^
      (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (c / 4)))
    ).toString(16)
  );
UUIDGeneratorBrowser(); // '7982fcfe-5721-4632-bede-6000885be57d'

生成UUID(Node.js)

在Node.JS中生成UUID。

  • 使用crypto.randomBytes()生成符合RFC4122版本4的UUID。
  • 使用Number.prototype.toString()将其转换为适当的UUID(十六进制字符串)。
  • crypto.randomUUID()提供类似的功能。
const crypto = require('crypto');

const UUIDGeneratorNode = () =>
  ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, c =>
    (c ^ (crypto.randomBytes(1)[0] & (15 >> (c / 4)))).toString(16)
  );
UUIDGeneratorNode(); // '79c7c136-60ee-40a2-beb2-856f1feabefc'

部分和 (累加)数组

创建一个部分和 数组。

  • 使用Array.prototype.reduce()用空数组累加器初始化,以迭代nums
  • 使用Array.prototype.slice()获取之前的部分和或0,并将当前元素添加到其中。
  • 使用点差运算符(...)将新的部分和添加到包含前一和的累加器数组中。
const accumulate = (...nums) =>
  nums.reduce((acc, n) => [...acc, n + (acc.slice(-1)[0] || 0)], []);
accumulate(1, 2, 3, 4); // [1, 3, 6, 10]
accumulate(...[1, 2, 3, 4]); // [1, 3, 6, 10]

将类添加到HTML元素

将类添加到HTML元素中。

  • 使用Element.classListDOMTokenList.add()将指定的类添加到元素中。
const addClass = (el, className) => el.classList.add(className);
addClass(document.querySelector('p'), 'special');
// 该段落现在将具有“特殊”类

增加日期

计算给定日期的n天的日期,返回其字符串表示形式。

  • 使用Date构造函数从第一个参数创建Date对象。
  • 使用Date.prototype.getDate()Date.prototype.setDate()为给定日期添加n天。
  • 使用Date.prototype.toISOString()返回yyyy-mm-dd格式的字符串。
const addDaysToDate = (date, n) => {
  const d = new Date(date);
  d.setDate(d.getDate() + n);
  return d.toISOString().split('T')[0];
};
addDaysToDate('2020-10-15', 10); // '2020-10-25'
addDaysToDate('2020-10-15', -10); // '2020-10-05'

将事件侦听器添加到所有目标

将事件侦听器附加到所有提供的目标。

  • 使用Array.prototype.forEach()EventTarget.addEventListener()将给定事件type提供listener附加到所有targets
const addEventListenerAll = (targets, type, listener, options, useCapture) => {
  targets.forEach(target =>
    target.addEventListener(type, listener, options, useCapture)
  );
};
addEventListenerAll(document.querySelectorAll('a'), 'click', () =>
  console.log('Clicked a link')
);
// 每当单击任何锚元素时,都会记录“单击链接”

向HTML元素添加样式

将提供的样式添加到给定的HTML元素中。

  • 使用Object.assign()HTMLElement.style将提供的styles对象合并到给定元素的样式中。
const addStyles = (el, styles) => Object.assign(el.style, styles);
addStyles(document.getElementById('my-element'), {
  background: 'red',
  color: '#ffff00',
  fontSize: '3rem'
});

将工作日添加到日期(计算工作日)

计算添加给定工作日数后的日期。

  • 使用Array.from()构造一个length等于要添加的工作日数的数组。
  • 使用Array.prototype.reduce()迭代数组,从startDate开始并增量,使用Date.prototype.getDate()Date.prototype.setDate()
  • 如果当前date在周末,请通过添加一天或两天来再次更新,使其成为工作日。
  • 注意:不考虑法定节假日。
const addWeekDays = (startDate, count) =>
  Array.from({ length: count }).reduce(date => {
    date = new Date(date.setDate(date.getDate() + 1));
    if (date.getDay() % 6 === 0)
      date = new Date(date.setDate(date.getDate() + (date.getDay() / 6 + 1)));
    return date;
  }, startDate);
addWeekDays(new Date('Oct 09, 2020'), 5); // 'Oct 16, 2020'
addWeekDays(new Date('Oct 12, 2020'), 5); // 'Oct 19, 2020'

测试所有数组元素是否都为真

检查提供的谓词函数是否对集合中的所有元素返回true

  • 使用Array.prototype.every()测试集合中的所有元素是否都基于fn返回true
  • 省略第二个参数fn,以使用Boolean作为默认值。
const all = (arr, fn = Boolean) => arr.every(fn);
all([4, 2, 3], x => x > 1); // true
all([1, 2, 3]); // true

检查数组元素是否相等

检查数组中的所有元素是否相等。

  • 使用Array.prototype.every()检查数组的所有元素是否与第一个元素相同。
  • 使用严格比较运算符比较数组中的元素,该运算符不考虑NaN自不相等。
const allEqual = arr => arr.every(val => val === arr[0]);
allEqual([1, 2, 3, 4, 5, 6]); // false
allEqual([1, 1, 1, 1]); // true

根据函数检查数组元素是否相等

根据提供的映射函数,检查数组中的所有元素是否相等。

  • fn应用于arr的第一个元素。
  • 使用Array.prototype.every()检查fn是否为数组中的所有元素返回与第一个元素相同的值。
  • 使用严格比较运算符比较数组中的元素,该运算符不考虑NaN自不相等。
const allEqualBy = (arr, fn) => {
  const eql = fn(arr[0]);
  return arr.every(val => fn(val) === eql);
};
allEqualBy([1.1, 1.2, 1.3], Math.round); // true
allEqualBy([1.1, 1.3, 1.6], Math.round); // false

检查所有数组元素是否都是唯一的

检查数组中的所有元素是否是唯一的。

  • 从映射值创建一个新的Set,以仅保留唯一的出现。
  • 使用Array.prototype.lengthSet.prototype.size将唯一值的长度与原始数组进行比较。
const allUnique = arr => arr.length === new Set(arr).size;
allUnique([1, 2, 3, 4]); // true
allUnique([1, 1, 2, 3]); // false

按字母顺序排序数组

根据给定属性按字母顺序对对象数组进行排序。

  • 使用Array.prototype.sort()根据给定的属性对数组进行排序。
  • 使用String.prototype.localeCompare()比较给定属性的值。
const alphabetical = (arr, getter, order = 'asc') =>
  arr.sort(
    order === 'desc'
      ? (a, b) => getter(b).localeCompare(getter(a))
      : (a, b) => getter(a).localeCompare(getter(b))
  );
const people = [ { name: 'John' }, { name: 'Adam' }, { name: 'Mary' } ];
alphabetical(people, g => g.name);
// [ { name: 'Adam' }, { name: 'John' }, { name: 'Mary' } ]
alphabetical(people, g => g.name, 'desc');
// [ { name: 'Mary' }, { name: 'John' }, { name: 'Adam' } ]

逻辑与(&&)

检查两个参数是否都true

  • 在两个给定值上使用逻辑和(&&)运算符。
const and = (a, b) => a && b;
and(true, true); // true
and(true, false); // false
and(false, false); // false

测试数组任意元素是否为真

检查提供的谓词函数是否对集合中的至少一个元素返回true

  • 使用Array.prototype.some()测试集合中的任何元素是否基于fn返回true
  • 省略第二个参数fn,以使用Boolean作为默认值。
const any = (arr, fn = Boolean) => arr.some(fn);
any([0, 1, 2, 0], x => x >= 2); // true
any([0, 0, 1, 0]); // true

连续元素子数组

创建一个n个连续元素的数组。

  • 使用Array.prototype.slice()Array.prototype.map()创建适当长度的数组。
  • arr的连续元素的n元组填充数组。
  • 如果n大于arr的长度,则返回一个空数组。
const aperture = (n, arr) =>
  n > arr.length
    ? []
    : arr.slice(n - 1).map((v, i) => arr.slice(i, i + n));
aperture(2, [1, 2, 3, 4]); // [[1, 2], [2, 3], [3, 4]]
aperture(3, [1, 2, 3, 4]); // [[1, 2, 3], [2, 3, 4]]
aperture(5, [1, 2, 3, 4]); // []

大约数字平等(约等于)

检查两个数字是否彼此大致相等。

  • 使用Math.abs()将两个值的绝对差异与epsilon进行比较。
  • 省略第三个参数epsilon,以使用0.001的默认值。
const approximatelyEqual = (v1, v2, epsilon = 0.001) =>
  Math.abs(v1 - v2) < epsilon;
approximatelyEqual(Math.PI / 2.0, 1.5708); // true

算术级数(生产等差数列)

在算术级数中创建一个数字数组,从给定的正整数开始,直到指定的极限。

  • 使用Array.from()创建一个所需长度的数组,lim / n。使用地图函数用给定范围内的所需值填充它。
const arithmeticProgression  = (n, lim) =>
  Array.from({ length: Math.ceil(lim / n) }, (_, i) => (i + 1) * n );
arithmeticProgression(5, 25); // [5, 10, 15, 20, 25]

数组转CSV

将二维数组转换为逗号分隔值(CSV)字符串。

  • 使用Array.prototype.map()Array.prototype.join()使用提供的delimiter将单个一维数组(行)组合成字符串。
  • 使用Array.prototype.join()将所有行组合成CSV字符串,用换行符(\n)分隔每行。
  • 省略第二个参数,delimiter,以使用默认分隔符。
const arrayToCSV = (arr, delimiter = ',') =>
  arr
    .map(v =>
      v.map(x => (isNaN(x) ? `"${x.replace(/"/g, '""')}"` : 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"'
arrayToCSV([['a', '"b" great'], ['c', 3.1415]]);
// '"a","""b"" great"\n"c",3.1415'

数组转HTML列表

将给定的数组元素转换为<li>标签,并将其附加到给定ID的列表中。

  • 使用Array.prototype.map()Document.querySelector()创建html标签列表。
const arrayToHTMLList = (arr, listID) =>
  document.querySelector(`#${listID}`).innerHTML += arr
    .map(item => `<li>${item}</li>`)
    .join('');
arrayToHTMLList(['item 1', 'item 2'], 'myListID');

功能性

创建一个最多接受n参数的函数,忽略任何其他参数。

  • 使用Array.prototype.slice()和扩展运算符(...)调用提供的函数fn,最多有n参数。
const ary = (fn, n) => (...args) => fn(...args.slice(0, n));
const firstTwoMax = ary(Math.max, 2);
[[2, 6, 'a'], [6, 4, 8], [10]].map(x => firstTwoMax(...x)); // [6, 6, 10]

验证对象键有效性

验证对象中的所有键与给定keys匹配。

  • 使用Object.keys()获取给定对象obj的键。
  • 使用Array.prototype.every()Array.prototype.includes()验证对象中的每个键是否在keys数组中指定。
const assertValidKeys = (obj, keys) =>
  Object.keys(obj).every(key => keys.includes(key));
assertValidKeys({ id: 10, name: 'apple' }, ['id', 'name']); // true
assertValidKeys({ id: 10, name: 'apple' }, ['id', 'type']); // false

解码Base64编码的字符串

解码已使用 base-64 编码编码的数据字符串。

  • Buffer使用 base-64 编码为给定的字符串创建一个。
  • 用于Buffer.prototype.toString()返回解码后的字符串。
const atob = str => Buffer.from(str, 'base64').toString('binary');
atob('Zm9vYmFy'); // 'foobar'

尝试调用函数并捕获错误

尝试使用提供的参数调用函数,返回结果或捕获的错误对象。

  • 使用try...catch块返回函数的结果或适当的错误。
  • 如果捕获的对象不是Error,则使用它创建一个新的Error
const 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 = []

计算平均数字

计算两个或多个数字的平均值。

  • 用于Array.prototype.reduce()将每个值添加到累加器,用值初始化0
  • 将结果数组除以其长度。
const average = (...nums) =>
  nums.reduce((acc, val) => acc + val, 0) / nums.length;
average(...[1, 2, 3]); // 2
average(1, 2, 3); // 2

映射数组平均值

在使用提供的函数将每个元素映射到一个值后,计算数组的平均值。

  • 用于Array.prototype.map()将每个元素映射到 返回的值fn
  • 用于Array.prototype.reduce()将每个值添加到累加器,用值初始化0
  • 将结果数组除以其长度。
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

基于值的拆封数组

根据给定数组的结果将值分成两组filter

  • 使用Array.prototype.reduce()Array.prototype.push()将元素添加到组中,基于filter.
  • 如果filter任何元素具有真值,则将其添加到第一组,否则将其添加到第二组。
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'] ]

基于函数的拆分数组

根据给定过滤函数的结果将值分成两组。

  • 根据每个元素返回的值,使用Array.prototype.reduce()and将元素添加到组中。Array.prototype.push()`fn`
  • 如果fn为任何元素返回真值,则将其添加到第一组,否则将其添加到第二组。
const bifurcateBy = (arr, fn) =>
  arr.reduce((acc, val, i) => (acc[fn(val, i) ? 0 : 1].push(val), acc), [
    [],
    [],
  ]);
bifurcateBy(['beep', 'boop', 'foo', 'bar'], x => x[0] === 'b');
// [ ['beep', 'boop', 'bar'], ['foo'] ]

两形参函数

创建一个最多接受两个参数的函数,忽略任何其他参数。

  • fn使用给定的前两个参数调用提供的函数。
const binary = fn => (a, b) => fn(a, b);
['2', '1', '0'].map(binary(Math.max)); // [2, 1, 2]

二分查找

使用二分法算法查找排序数组中给定元素的索引。

  • 声明左右搜索边界,l,分别r初始化为数组的0和。length
  • 使用while循环重复缩小搜索子数组,使用Math.floor()将其切成两半。
  • 如果找到,则返回元素的索引,否则返回-1
  • 注意:不考虑数组中的重复值。
const binarySearch = (arr, item) => {
  let l = 0,
    r = arr.length - 1;
  while (l <= r) {
    const mid = Math.floor((l + r) / 2);
    const guess = arr[mid];
    if (guess === item) return mid;
    if (guess > item) r = mid - 1;
    else l = mid + 1;
  }
  return -1;
};
binarySearch([1, 2, 3, 4, 5], 1); // 0
binarySearch([1, 2, 3, 4, 5], 5); // 4
binarySearch([1, 2, 3, 4, 5], 6); // -1

绑定函数上下文

创建一个调用fn给定上下文的函数,可以选择将任何额外提供的参数添加到参数中。

  • 返回一个function用于将给定Function.prototype.apply()应用于.context`fn`
  • 使用扩展运算符 ( ...) 将任何额外提供的参数添加到参数中。
const bind = (fn, context, ...boundArgs) => (...args) =>
  fn.apply(context, [...boundArgs, ...args]);
function greet(greeting, punctuation) {
  return greeting + ' ' + this.user + punctuation;
}
const freddy = { user: 'fred' };
const freddyBound = bind(greet, freddy);
console.log(freddyBound('hi', '!')); // 'hi fred!'

绑定所有对象方法

将对象的方法绑定到对象本身,覆盖现有方法。

  • 用于Array.prototype.forEach()迭代给定的fns.
  • 为每个函数返回一个函数,用于Function.prototype.apply()将给定的上下文 ( obj)应用于fn
const bindAll = (obj, ...fns) =>
  fns.forEach(
    fn => (
      (f = obj[fn]),
      (obj[fn] = function() {
        return f.apply(obj);
      })
    )
  );
var view = {
  label: 'docs',
  click: function() {
    console.log('clicked ' + this.label);
  }
};
bindAll(view, 'click');
document.body.addEventListener('click', view.click);
// 单击时记录“单击的文档”

绑定对象方法

创建一个函数,该函数在对象的给定键处调用该方法,可以选择将任何其他提供的参数添加到参数中。

  • 返回一个function用于Function.prototype.apply()绑定context[fn]context.
  • 使用扩展运算符 ( ...) 将任何额外提供的参数添加到参数中。
const bindKey = (context, fn, ...boundArgs) => (...args) =>
  context[fn].apply(context, [...boundArgs, ...args]);
const freddy = {
  user: 'fred',
  greet: function(greeting, punctuation) {
    return greeting + ' ' + this.user + punctuation;
  }
};
const freddyBound = bindKey(freddy, 'greet');
console.log(freddyBound('hi', '!')); // 'hi fred!'

二项式系数

计算从项目中无重复无顺序地选择k项目的方法数。n

  • 用于Number.isNaN()检查两个值中的任何一个是否为NaN
  • 检查是否k小于0、大于或等于n、等于1n - 1并返回适当的结果。
  • 检查是否n - k小于k并相应地切换它们的值。
  • 2从中循环k并计算二项式系数。
  • 用于Math.round()说明计算中的舍入误差。
const binomialCoefficient = (n, k) => {
  if (Number.isNaN(n) || Number.isNaN(k)) return NaN;
  if (k < 0 || k > n) return 0;
  if (k === 0 || k === n) return 1;
  if (k === 1 || k === n - 1) return n;
  if (n - k < k) k = n - k;
  let res = n;
  for (let j = 2; j <= k; j++) res *= (n - j + 1) / j;
  return Math.round(res);
};
binomialCoefficient(8, 2); // 28

逻辑与函数

检查两个给定的函数是否返回true一组给定的参数。

  • &&在使用提供的 调用这两个函数的结果上使用逻辑 and ( ) 运算符args
const both = (f, g) => (...args) => f(...args) && g(...args);
const isEven = num => num % 2 === 0;
const isPositive = num => num > 0;
const isPositiveEven = both(isEven, isPositive);
isPositiveEven(4); // true
isPositiveEven(-2); // false

检查页面底部是否可见

检查页面底部是否可见。

  • 使用Window.scrollY,Element.scrollHeightElement.clientHeight确定页面底部是否可见。
const bottomVisible = () =>
  document.documentElement.clientHeight + window.scrollY >=
  (document.documentElement.scrollHeight ||
    document.documentElement.clientHeight);
bottomVisible(); // true

将字符串编码为 Base64

从 String 对象创建一个 base-64 编码的 ASCII 字符串,其中字符串中的每个字符都被视为二进制数据的一个字节。

  • Buffer使用二进制编码为给定的字符串创建一个。
  • 用于Buffer.prototype.toString()返回 base-64 编码的字符串。
const btoa = str => Buffer.from(str, 'binary').toString('base64');
btoa('foobar'); // 'Zm9vYmFy'

冒泡排序

使用冒泡排序算法对数字数组进行排序。

  • 声明一个变量 ,swapped它指示在当前迭代期间是否交换了任何值。
  • 使用扩展运算符 ( ...) 克隆原始数组arr.
  • 使用for循环遍历克隆数组的元素,在最后一个元素之前终止。
  • 使用嵌套循环迭代和for之间的数组段,交换任何相邻的乱序元素并设置为。0`iswappedtrue`
  • 如果swapped是在false迭代之后,则不需要再进行更改,因此返回克隆的数组。
const bubbleSort = arr => {
  let swapped = false;
  const a = [...arr];
  for (let i = 1; i < a.length; i++) {
    swapped = false;
    for (let j = 0; j < a.length - i; j++) {
      if (a[j + 1] < a[j]) {
        [a[j], a[j + 1]] = [a[j + 1], a[j]];
        swapped = true;
      }
    }
    if (!swapped) return a;
  }
  return a;
};
bubbleSort([2, 1, 4, 3]); // [1, 2, 3, 4]

桶排序(箱排序

使用桶排序算法对数字数组进行排序。

  • 使用Math.min(),Math.max()和展开运算符 ( ...) 查找给定数组的最小值和最大值。
  • 使用Array.from()andMath.floor()创建适当数量的buckets(空数组)。
  • 用于使用Array.prototype.forEach()数组中的适当元素填充每个桶。
  • 使用Array.prototype.reduce()、传播运算符 ( ...) 和Array.prototype.sort()对每个桶进行排序并将其附加到结果中。
const bucketSort = (arr, size = 5) => {
  const min = Math.min(...arr);
  const max = Math.max(...arr);
  const buckets = Array.from(
    { length: Math.floor((max - min) / size) + 1 },
    () => []
  );
  arr.forEach(val => {
    buckets[Math.floor((val - min) / size)].push(val);
  });
  return buckets.reduce((acc, b) => [...acc, ...b.sort((a, b) => a - b)], []);
};
bucketSort([6, 3, 4, 1]); // [1, 3, 4, 6]

字符串的字节大小

以字节为单位返回字符串的长度。

  • 将给定的字符串转换为BlobObject
  • 用于Blob.size获取字符串的字节长度。
const byteSize = str => new Blob([str]).size;
byteSize('?'); // 4
byteSize('Hello World'); // 11

凯撒密码

使用凯撒密码加密或解密给定的字符串。

  • 使用模 ( %) 运算符和三元运算符 ( ?) 来计算正确的加密/解密密钥。
  • 使用扩展运算符 ( ...) 和Array.prototype.map()迭代给定字符串的字母。
  • 使用String.prototype.charCodeAt()andString.fromCharCode()适当地转换每个字母,忽略特殊字符、空格等。
  • 用于Array.prototype.join()将所有字母组合成一个字符串。
  • 传递true给最后一个参数 ,decrypt以解密加密的字符串。
const caesarCipher = (str, shift, decrypt = false) => {
  const s = decrypt ? (26 - shift) % 26 : shift;
  const n = s > 0 ? s : 26 + (s % 26);
  return [...str]
    .map((l, i) => {
      const c = str.charCodeAt(i);
      if (c >= 65 && c <= 90)
        return String.fromCharCode(((c - 65 + n) % 26) + 65);
      if (c >= 97 && c <= 122)
        return String.fromCharCode(((c - 97 + n) % 26) + 97);
      return l;
    })
    .join('');
};
caesarCipher('Hello World!', -3); // 'Ebiil Tloia!'
caesarCipher('Ebiil Tloia!', 23, true); // 'Hello World!'

使用上下文调用函数

给定一个键和一组参数,在给定上下文时调用它们。

  • 使用闭包调用keyargs定的context.
const call = (key, ...args) => context => context[key](...args);
Promise.resolve([1, 2, 3])
  .then(call('map', x => 2 * x))
  .then(console.log); // [ 2, 4, 6 ]
const map = call.bind(null, 'map');
Promise.resolve([1, 2, 3])
  .then(map(x => 2 * x))
  .then(console.log); // [ 2, 4, 6 ]

调用或返回

如果参数是函数则调用它,否则返回它。

  • 使用typeof运算符检查给定参数是否为函数。
  • 如果是,则使用扩展运算符 ( ...) 将其与给定参数的其余部分一起调用。否则,退货。
const callOrReturn = (fn, ...args) =>
  typeof fn === 'function' ? fn(...args) : fn;

callOrReturn(x => x + 1, 1); // 2
callOrReturn(1, 1); // 1

大写字符串

将字符串的第一个字母大写。

  • 使用数组解构并将String.prototype.toUpperCase()字符串的第一个字母大写。
  • 用于Array.prototype.join()组合大写字母first...rest字符的。
  • 省略lowerRest参数以保持字符串的其余部分完整,或将其设置true为转换为小写。
const capitalize = ([first, ...rest], lowerRest = false) =>
  first.toUpperCase() +
  (lowerRest ? rest.join('').toLowerCase() : rest.join(''));
capitalize('fooBar'); // 'FooBar'
capitalize('fooBar', true); // 'Foobar'

大写每个字

将字符串中每个单词的首字母大写。

  • 用于String.prototype.replace()匹配每个单词的第一个字符并将String.prototype.toUpperCase()其大写。
const capitalizeEveryWord = str =>
  str.replace(/\b[a-z]/g, char => char.toUpperCase());
capitalizeEveryWord('hello world!'); // 'Hello World!'

笛卡尔乘积(直

计算两个数组的笛卡尔积。

  • 使用Array.prototype.reduce(),Array.prototype.map()和展开运算符 ( ...) 从两个数组中生成所有可能的元素对。
const cartesianProduct = (a, b) =>
  a.reduce((p, x) => [...p, ...b.map(y => [x, y])], []);

cartesianProduct(['x', 'y'], [1, 2]);
// [['x', 1], ['x', 2], ['y', 1], ['y', 2]]

转换为数组

如果提供的值不是一个,则将其转换为数组。

  • 用于Array.isArray()判断是否val是一个数组并按原样返回或相应地封装在一个数组中。
const castArray = val => (Array.isArray(val) ? val : [val]);
castArray('foo'); // ['foo']
castArray([1]); // [1]

摄氏度转华氏度

将摄氏度转换为华氏度。

  • 遵循转换公式F = 1.8 * C + 32
const celsiusToFahrenheit = degrees => 1.8 * degrees + 32;
celsiusToFahrenheit(33); // 91.4

链式异步调用函数

链接异步函数。

  • 循环遍历包含异步事件的函数数组,next在每个异步事件完成时调用。
const chainAsync = fns => {
  let curr = 0;
  const last = fns[fns.length - 1];
  const next = () => {
    const fn = fns[curr++];
    fn === last ? fn() : fn(next);
  };
  next();
};
chainAsync([
  next => {
    console.log('0 seconds');
    setTimeout(next, 1000);
  },
  next => {
    console.log('1 second');
    setTimeout(next, 1000);
  },
  () => {
    console.log('2 second');
  }
]);

改变颜色亮度

更改颜色字符串的亮度值hsl()

  • 用于String.prototype.match()获取包含数值的 3 个字符串的数组。
  • Array.prototype.map()结合使用将Number它们转换为数值数组。
  • 使用和确保亮度在有效范围内(介于0和之间100)。Math.max()`Math.min()`
  • 使用模板文字创建hsl()具有更新值的新字符串。
const changeLightness = (delta, hslStr) => {
  const [hue, saturation, lightness] = hslStr.match(/\d+/g).map(Number);

  const newLightness = Math.max(
    0,
    Math.min(100, lightness + parseFloat(delta))
  );

  return `hsl(${hue}, ${saturation}%, ${newLightness}%)`;
};
changeLightness(10, 'hsl(330, 50%, 50%)'); // 'hsl(330, 50%, 60%)'
changeLightness(-10, 'hsl(330, 50%, 50%)'); // 'hsl(330, 50%, 40%)'

检查属性

创建一个函数,该函数将为给定对象的指定属性调用谓词函数。

  • 返回一个 curried 函数,它将调用predicate指定的proponobj并返回一个布尔值。
const checkProp = (predicate, prop) => obj => !!predicate(obj[prop]);
const lengthIs4 = checkProp(l => l === 4, 'length');
lengthIs4([]); // false
lengthIs4([1, 2, 3, 4]); // true
lengthIs4(new Set([1, 2, 3, 4])); // false (Set uses Size, not length)

const session = { user: {} };
const validUserSession = checkProp(u => u.active && !u.disabled, 'user');

validUserSession(session); // false

session.user.active = true;
validUserSession(session); // true

const noLength = checkProp(l => l === undefined, 'length');
noLength([]); // false
noLength({}); // true
noLength(new Set()); // true

分割成指定长度数组

将数组分块为指定大小的较小数组。

  • 用于Array.from()创建一个新数组,该数组适合将要生成的块数。
  • 用于Array.prototype.slice()将新数组的每个元素映射到长度为 的块size
  • 如果原始数组不能被平均分割,最后的块将包含剩余的元素。
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]]

将数组拆分成 n 个数组

将数组分成n更小的数组。

  • 使用Math.ceil()andArray.prototype.length获取每个块的大小。
  • 用于Array.from()创建一个新的大小数组n
  • 用于Array.prototype.slice()将新数组的每个元素映射到长度为 的块size
  • 如果原始数组不能被平均分割,最后的块将包含剩余的元素。
const chunkIntoN = (arr, n) => {
  const size = Math.ceil(arr.length / n);
  return Array.from({ length: n }, (v, i) =>
    arr.slice(i * size, i * size + size)
  );
}
chunkIntoN([1, 2, 3, 4, 5, 6, 7], 4); // [[1, 2], [3, 4], [5, 6], [7]]

可迭代对象分成指定大小的数组

将可迭代对象分成指定大小的较小数组。

  • for...of在给定的 iterable 上使用循环,用于Array.prototype.push()将每个新值添加到当前chunk.
  • 用于Array.prototype.length检查电流是否chunk符合要求size以及yield是否符合要求。
  • 最后,用于Array.prototype.length检查 finalchunkyield它是否为非空。
const chunkify = function* (itr, size) {
  let chunk = [];
  for (const v of itr) {
    chunk.push(v);
    if (chunk.length === size) {
      yield chunk;
      chunk = [];
    }
  }
  if (chunk.length) yield chunk;
};

const x = new Set([1, 2, 1, 3, 4, 1, 2, 5]);
[...chunkify(x, 2)]; // [[1, 2], [3, 4], [5]]

找出中间数

夹在由边界值和num指定的包含范围内。a `b`

  • 如果num在范围内,则返回num
  • 否则,返回范围内最接近的数字。
const clampNumber = (num, a, b) =>
  Math.max(Math.min(num, Math.max(a, b)), Math.min(a, b));
clampNumber(2, 3, 5); // 3
clampNumber(1, -1, -5); // -1

克隆正则表达式

克隆正则表达式。

  • 使用RegExp构造函数,RegExp.prototype.sourceRegExp.prototype.flags克隆给定的正则表达式。
const cloneRegExp = regExp => new RegExp(regExp.source, regExp.flags);
const regExp = /lorem ipsum/gi;
const regExp2 = cloneRegExp(regExp); // regExp !== regExp2

最接近的数字匹配

从数组中查找最接近的数字。

  • 用于Array.prototype.reduce()扫描数组的所有元素。
  • 用于Math.abs()比较每个元素与目标值的距离,存储最接近的匹配。
const closest = (arr, n) =>
  arr.reduce((acc, num) => (Math.abs(num - n) < Math.abs(acc - n) ? num : acc));
closest([6, 1, 3, 7, 9], 5); // 6

参数合并(去除null 和undefined)

返回第一个定义的非空参数。

  • 使用Array.prototype.find()和`Array.prototype.includes()查找不等于undefined和null`的第一个值。
const coalesce = (...args) => args.find(v => ![undefined, null].includes(v));
coalesce(null, undefined, '', NaN, 'Waldo'); // ''

参数合并工厂(去除undefined, null, NaN, 空字符串)

自定义一个合并函数,该函数根据给定的验证器返回第一个为真的参数。
  • 使用 Array.prototype.find() 返回从提供的参数验证函数返回 true 的第一个参数,valid。
const coalesceFactory = valid => (...args) => args.find(valid);
const customCoalesce = coalesceFactory(
  v => ![null, undefined, '', NaN].includes(v)
);
customCoalesce(undefined, null, NaN, '', 'Waldo'); // 'Waldo'

将函数参数转换为可变参数

将接受数组的函数更改为可变参数函数。

  • 给定一个函数,返回一个将所有输入收集到数组接受函数中的闭包。
const collectInto = fn => (...args) => fn(args);
const Pall = collectInto(Promise.all.bind(Promise));
let p1 = Promise.resolve(1);
let p2 = Promise.resolve(2);
let p3 = new Promise(resolve => setTimeout(resolve, 2000, 3));
Pall(p1, p2, p3).then(console.log); // [1, 2, 3] (after about 2 seconds)

给文本着色

向文本添加特殊字符以在控制台中以彩色打印(与 结合console.log())。

  • 使用模板文字和特殊字符将适当的颜色代码添加到字符串输出。
  • 对于背景颜色,添加一个特殊字符,用于在字符串末尾重置背景颜色。
const colorize = (...args) => ({
  black: `\x1b[30m${args.join(' ')}`,
  red: `\x1b[31m${args.join(' ')}`,
  green: `\x1b[32m${args.join(' ')}`,
  yellow: `\x1b[33m${args.join(' ')}`,
  blue: `\x1b[34m${args.join(' ')}`,
  magenta: `\x1b[35m${args.join(' ')}`,
  cyan: `\x1b[36m${args.join(' ')}`,
  white: `\x1b[37m${args.join(' ')}`,
  bgBlack: `\x1b[40m${args.join(' ')}\x1b[0m`,
  bgRed: `\x1b[41m${args.join(' ')}\x1b[0m`,
  bgGreen: `\x1b[42m${args.join(' ')}\x1b[0m`,
  bgYellow: `\x1b[43m${args.join(' ')}\x1b[0m`,
  bgBlue: `\x1b[44m${args.join(' ')}\x1b[0m`,
  bgMagenta: `\x1b[45m${args.join(' ')}\x1b[0m`,
  bgCyan: `\x1b[46m${args.join(' ')}\x1b[0m`,
  bgWhite: `\x1b[47m${args.join(' ')}\x1b[0m`
});

console.log(colorize('foo').red); // 'foo' (红色字母)
console.log(colorize('foo', 'bar').bgBlue); // 'foo bar' (蓝色背景)
console.log(colorize(colorize('foo').yellow, colorize('foo').green).bgWhite);
// 'foo bar' (黄色字母的第一个单词,绿色字母的第二个单词,两者均为白色背景)

合并对象数组

组合两个对象数组,使用指定的键来匹配对象。

  • 与对象累加器一起使用,Array.prototype.reduce()根据给定的 组合两个数组中的所有对象prop
  • 用于Object.values()将生成的对象转换为数组并将其返回。
const combine = (a, b, prop) =>
  Object.values(
    [...a, ...b].reduce((acc, v) => {
      if (v[prop])
        acc[v[prop]] = acc[v[prop]]
          ? { ...acc[v[prop]], ...v }
          : { ...v };
      return acc;
    }, {})
  );
const x = [
  { id: 1, name: 'John' },
  { id: 2, name: 'Maria' }
];
const y = [
  { id: 1, age: 28 },
  { id: 3, age: 26 },
  { age: 3}
];
combine(x, y, 'id');
// [
//  { id: 1, name: 'John', age: 28 },
//  { id: 2, name: 'Maria' },
//  { id: 3, age: 26 }
// ]

查找公共键

查找两个对象之间的公共键。

  • 用于Object.keys()获取第一个对象的键。
  • 用于Object.prototype.hasOwnProperty()检查第二个对象是否具有第一个对象中的键。
  • 用于Array.prototype.filter()过滤掉不在两个对象中的键。
const commonKeys = (obj1, obj2) =>
  Object.keys(obj1).filter(key => obj2.hasOwnProperty(key));
commonKeys({ a: 1, b: 2 }, { a: 2, c: 1 }); // ['a']

删除数组假值

从数组中删除虚假值。

  • 用于Array.prototype.filter()过滤掉虚假值(falsenull0""undefinedNaN)。
const compact = arr => arr.filter(Boolean);
compact([0, 1, false, 2, '', 3, 'a', 'e' * 23, NaN, 's', 34]);
// [ 1, 2, 3, 'a', 's', 34 ]

删除数组假值并拼接成字符串

从数组中删除虚假值并将剩余值组合成一个字符串。

  • 用于Array.prototype.filter()过滤掉虚假值(falsenull0""undefinedNaN)。
  • 用于Array.prototype.join()将剩余值连接成一个字符串。
const compactJoin = (arr, delim = ',') => arr.filter(Boolean).join(delim);
compactJoin(['a', '', 'b', 'c']); // 'a,b,c'

删除对象假值

从对象或数组中深度删除所有虚假值。

  • 使用递归。
  • Array.isArray()使用和Array.prototype.filter()为数组初始化可迭代数据Boolean以避免稀疏数组。
  • 使用Object.keys()andArray.prototype.reduce()以适当的初始值迭代每个键。
  • 用于Boolean确定每个键值的真实性,如果是真实的,则将其添加到累加器。
  • 用于typeof确定给定值是否为 anobject并再次调用该函数以对其进行深度压缩。
const compactObject = val => {
  const data = Array.isArray(val) ? val.filter(Boolean) : val;
  return Object.keys(data).reduce(
    (acc, key) => {
      const value = data[key];
      if (Boolean(value))
        acc[key] = typeof value === 'object' ? compactObject(value) : value;
      return acc;
    },
    Array.isArray(val) ? [] : {}
  );
};
const obj = {
  a: null,
  b: false,
  c: true,
  d: 0,
  e: 1,
  f: '',
  g: 'a',
  h: [null, false, '', true, 1, 'a'],
  i: { j: 0, k: false, l: 'a' }
};
compactObject(obj);
// { c: true, e: 1, g: 'a', h: [ true, 1, 'a' ], i: { l: 'a' } }

压缩空格

压缩字符串中多余的空格。

  • 与正则表达式一起使用,String.prototype.replace()将所有出现的 2 个或更多空白字符替换为一个空格。
const compactWhitespace = str => str.replace(/\s{2,}/g, ' ');
compactWhitespace('Lorem    Ipsum'); // 'Lorem Ipsum'
compactWhitespace('Lorem \n Ipsum'); // 'Lorem Ipsum'

逻辑补充

返回一个函数,该函数是给定函数的逻辑补集fn

  • !对调用fn任何提供的结果使用逻辑 not ( ) 运算符args
const complement = fn => (...args) => !fn(...args);
const isEven = num => num % 2 === 0;
const isOdd = complement(isEven);
isOdd(2); // false
isOdd(3); // true

组合函数

执行从右到左的函数组合。

  • 用于Array.prototype.reduce()执行从右到左的函数组合。
  • 最后一个(最右边的)函数可以接受一个或多个参数;其余函数必须是一元的。
const compose = (...fns) =>
  fns.reduce((f, g) => (...args) => f(g(...args)));
const add5 = x => x + 5;
const multiply = (x, y) => x * y;
const multiplyAndAdd5 = compose(
  add5,
  multiply
);
multiplyAndAdd5(5, 2); // 15

反向组合函数

执行从左到右的函数组合。

  • 用于Array.prototype.reduce()执行从左到右的函数组合。
  • 第一个(最左边的)函数可以接受一个或多个参数;其余函数必须是一元的。
const composeRight = (...fns) =>
  fns.reduce((f, g) => (...args) => g(f(...args)));
const add = (x, y) => x + y;
const square = x => x * x;
const addAndSquare = composeRight(add, square);
addAndSquare(1, 2); // 9

检查字符串是否包含空格

检查给定的字符串是否包含任何空白字符。

  • 使用RegExp.prototype.test()适当的正则表达式来检查给定的字符串是否包含任何空白字符。
const containsWhitespace = str => /\s/.test(str);
containsWhitespace('lorem'); // false
containsWhitespace('lorem ipsum'); // true

收敛分支函数

接受一个收敛函数和一个分支函数列表,并返回一个将每个分支函数应用于参数的函数,并将分支函数的结果作为参数传递给收敛函数。

  • 使用Array.prototype.map()andFunction.prototype.apply()将每个函数应用于给定的参数。
  • 使用扩展运算符 ( ...) 调用converger所有其他函数的结果。
const converge = (converger, fns) => (...args) =>
  converger(...fns.map(fn => fn.apply(null, args)));
const average = converge((a, b) => a / b, [
  arr => arr.reduce((a, v) => a + v, 0),
  arr => arr.length
]);
average([1, 2, 3, 4, 5, 6, 7]); // 4

将符号复制到数字

返回第一个数字的绝对值,但返回第二个数字的符号。

  • 用于Math.sign()检查两个数字是否具有相同的符号。
  • x如果他们这样做,请返回,-x否则。
const copySign = (x, y) => Math.sign(x) === Math.sign(y) ? x : -x;
copySign(2, 3); // 2
copySign(2, -3); // -2
copySign(-2, 3); // 2
copySign(-2, -3); // -2

复制到剪贴板

将字符串复制到剪贴板。仅作为用户操作的结果(即在click事件侦听器中)起作用。

  • 创建一个新<textarea>元素,用提供的数据填充它并将它添加到 HTML 文档中。
  • 用于Selection.getRangeAt()存储所选范围(如果有)。
  • 用于Document.execCommand()复制到剪贴板。
  • <textarea>从 HTML 文档中删除该元素。
  • 最后,使用Selection.addRange()恢复原始选定范围(如果有)。
  • 注意:您可以在大多数当前浏览器中使用异步剪贴板 API
const copyToClipboard = str => {
  const el = document.createElement('textarea');
  el.value = str;
  el.setAttribute('readonly', '');
  el.style.position = 'absolute';
  el.style.left = '-9999px';
  document.body.appendChild(el);
  const selected =
    document.getSelection().rangeCount > 0
      ? document.getSelection().getRangeAt(0)
      : false;
  el.select();
  document.execCommand('copy');
  document.body.removeChild(el);
  if (selected) {
    document.getSelection().removeAllRanges();
    document.getSelection().addRange(selected);
  }
};
copyToClipboard('Lorem ipsum'); // 'Lorem ipsum' 复制到剪贴板。

异步复制到剪贴板

将字符串复制到剪贴板,返回一个在剪贴板内容更新后解析的承诺。

  • 检查剪贴板 API是否可用。使用if声明来确保NavigatorNavigator.clipboardNavigator.clipboard.writeText真实的。
  • 用于Clipboard.writeText()将给定值 写入str剪贴板。
  • 返回 的结果Clipboard.writeText(),这是一个在剪贴板内容更新后解析的承诺。
  • 如果剪贴板 API 不可用,请使用Promise.reject()来拒绝并显示适当的消息。
  • 注意:如果您需要支持旧版浏览器,则可能需要使用Document.execCommand()
const copyToClipboardAsync = str => {
  if (navigator && navigator.clipboard && navigator.clipboard.writeText)
    return navigator.clipboard.writeText(str);
  return Promise.reject('剪贴板 API 不可用');
};
copyToClipboardAsync('Lorem ipsum'); // 'Lorem ipsum'已复制到剪贴板。

计算分组元素

根据给定的函数对数组的元素进行分组,并返回每组中元素的数量。

  • 用于Array.prototype.map()将数组的值映射到函数或属性名称。
  • 用于Array.prototype.reduce()创建一个对象,其中的键是从映射结果中生成的。
const countBy = (arr, fn) =>
  arr.map(typeof fn === 'function' ? fn : val => val[fn]).reduce((acc, val) => {
    acc[val] = (acc[val] || 0) + 1;
    return acc;
  }, {});
countBy([6.1, 4.2, 6.3], Math.floor); // {4: 1, 6: 2}
countBy(['one', 'two', 'three'], 'length'); // {3: 2, 5: 1}
countBy([{ count: 5 }, { count: 10 }, { count: 5 }], x => x.count)
// {5: 2, 10: 1}

计算出现次数

计算数组中某个值的出现次数。

  • 每次在数组中遇到特定值时用于Array.prototype.reduce()递增计数器。
const countOccurrences = (arr, val) =>
  arr.reduce((a, v) => (v === val ? a + 1 : a), 0);
countOccurrences([1, 1, 2, 1, 2, 3], 1); // 3

计算字符串的子串出现次数

计算给定字符串中子字符串的出现次数。

  • 用于Array.prototype.indexOf()在. searchValue_str
  • 如果找到值并更新索引,则增加计数器i
  • 使用一个while循环,只要从 返回的值Array.prototype.indexOf()是 就会返回-1
const countSubstrings = (str, searchValue) => {
  let count = 0,
    i = 0;
  while (true) {
    const r = str.indexOf(searchValue, i);
    if (r !== -1) [count, i] = [count + 1, r + 1];
    else return count;
  }
};
countSubstrings('tiktok tok tok tik tok tik', 'tik'); // 3
countSubstrings('tutut tut tut', 'tut'); // 4

计数器

为指定的选择器创建一个具有指定范围、步长和持续时间的计数器。

  • 检查是否step有正确的标志并相应地更改它。
  • setInterval()Math.abs()和结合使用Math.floor()以计算每次绘制新文本之间的时间。
  • 使用Document.querySelector(),Element.innerHTML更新所选元素的值。
  • 省略第四个参数 ,step以使用默认步骤1
  • 省略第五个参数 ,duration以使用默认持续时间2000ms。
const counter = (selector, start, end, step = 1, duration = 2000) => {
  let current = start,
    _step = (end - start) * step < 0 ? -step : step,
    timer = setInterval(() => {
      current += _step;
      document.querySelector(selector).innerHTML = current;
      if (current >= end) document.querySelector(selector).innerHTML = end;
      if (current >= end) clearInterval(timer);
    }, Math.abs(Math.floor(duration / (end - start))));
  return timer;
};
counter('#my-id', 1, 1000, 5, 2000);
// Creates a 2-second timer for the element with id="my-id"

计算两个日期之间的工作日

计算两个日期之间的工作日。

  • 用于Array.from()构造一个等于和length之间的天数的数组。startDate`endDate`
  • 用于Array.prototype.reduce()遍历数组,检查每个日期是否为工作日并递增count
  • startDate第二天更新每个循环使用Date.prototype.getDate()Date.prototype.setDate()提前一天。
  • 注意:不考虑法定节假日。
const countWeekDaysBetween = (startDate, endDate) =>
  Array
    .from({ length: (endDate - startDate) / (1000 * 3600 * 24) })
    .reduce(count => {
      if (startDate.getDay() % 6 !== 0) count++;
      startDate = new Date(startDate.setDate(startDate.getDate() + 1));
      return count;
    }, 0);
countWeekDaysBetween(new Date('Oct 05, 2020'), new Date('Oct 06, 2020')); // 1
countWeekDaysBetween(new Date('Oct 05, 2020'), new Date('Oct 14, 2020')); // 7

如果不存在则创建目录

创建目录(如果不存在)。

  • 用于fs.existsSync()检查目录是否存在,fs.mkdirSync()以创建它。
const fs = require('fs');

const createDirIfNotExists = dir =>
  !fs.existsSync(dir) ? fs.mkdirSync(dir) : undefined;
createDirIfNotExists('test');
// creates the directory 'test', if it doesn't exist

创建 HTML 元素

从字符串创建一个元素(不将其附加到文档)。如果给定的字符串包含多个元素,则只返回第一个。

  • 用于Document.createElement()创建新元素。
  • 用于Element.innerHTML将其内部 HTML 设置为作为参数提供的字符串。
  • 用于Element.firstElementChild返回字符串的元素版本。
const createElement = str => {
  const el = document.createElement('div');
  el.innerHTML = str;
  return el.firstElementChild;
};
const el = createElement(
  `<div class="container">
    <p>Hello!</p>
  </div>`
);
console.log(el.className); // 'container'

创建事件中心

使用 emit、on 和 off 方法创建发布/订阅` ([发布-订阅](https://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern))`事件中心。
  • 使用Object.create()创建一个参数为null的空对象hub,该对象不从 Object.prototype 继承属性。
  • 对于 emit,根据事件参数解析处理程序数组,然后通过传入数据event作为参数使用Array.prototype.forEach() 运行每个处理程序。
  • 对于on,如果事件尚不存在则为其创建一个数组,然后用于Array.prototype.push()添加处理程序到数组中。
  • 对于off,使用 Array.prototype.findIndex() 在事件数组中查找处理程序的索引,并使用 Array.prototype.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' 和 消息事件被触发
hub.emit('message', { hello: 'world' }); // 记录对象和“触发消息事件”
hub.emit('increment'); // `increment` 变量现在是 1

// 取消订阅:停止特定处理程序监听“消息”事件
hub.off('message', handler);

当前网址(URL)

返回当前 URL。

  • 用于Window.location.href获取当前 URL。
const currentURL = () => window.location.href;
currentURL(); // 'https://www.google.com/'

函数柯里化

柯里化一个函数。

  • 使用递归。
  • 如果提供的参数 () 数量args足够,则调用传递的函数fn
  • 否则,使用Function.prototype.bind()返回一个fn需要其余参数的柯里化函数。
  • 如果您想柯里化一个接受可变数量参数的函数(可变参数函数,例如Math.min()),您可以选择将参数数量传递给第二个参数arity
const curry = (fn, arity = fn.length, ...args) =>
  arity <= args.length ? fn(...args) : curry.bind(null, fn, arity, ...args);
curry(Math.pow)(2)(10); // 1024
curry(Math.min, 3)(10)(50)(2); // 2

循环生成器

创建一个生成器,无限循环遍历给定的数组。

  • 使用非终止while循环,yield每次Generator.prototype.next()调用都会有一个值。
  • 使用模块运算符 ( %) withArray.prototype.length获取下一个值的索引并在每个yield语句后递增计数器。
const cycleGenerator = function* (arr) {
  let i = 0;
  while (true) {
    yield arr[i % arr.length];
    i++;
  }
};
const binaryCycle = cycleGenerator([0, 1]);
binaryCycle.next(); // { value: 0, done: false }
binaryCycle.next(); // { value: 1, done: false }
binaryCycle.next(); // { value: 0, done: false }
binaryCycle.next(); // { value: 1, done: false }

日期范围生成器

创建一个生成器,它使用给定步骤生成给定范围内的所有日期。

  • 使用while循环从start到迭代end,使用yield返回范围内的每个日期,使用Date构造函数。
  • 使用Date.prototype.getDate()and在返回每个后续值后Date.prototype.setDate()按天递增step
  • 省略第三个参数 ,step以使用默认值1
const dateRangeGenerator = function* (start, end, step = 1) {
  let d = start;
  while (d < end) {
    yield new Date(d);
    d.setDate(d.getDate() + step);
  }
};
[...dateRangeGenerator(new Date('2021-06-01'), new Date('2021-06-04'))];
// [ 2021-06-01, 2021-06-02, 2021-06-03 ]

(日期名称)获取星期几

从对象中获取星期几的名称Date

  • Date.prototype.toLocaleDateString()与选项一起使用{ weekday: 'long' }以检索工作日。
  • 使用可选的第二个参数来获取特定于语言的名称或省略它以使用默认区域设置。
const dayName = (date, locale) =>
  date.toLocaleDateString(locale, { weekday: 'long' });
dayName(new Date()); // 'Saturday'
dayName(new Date('09/23/2020'), 'de-DE'); // 'Samstag'

一年中的某一天

从对象中获取一年中的第几天(1-366 范围内的数字)Date

  • 使用Date构造函数并Date.prototype.getFullYear()获取一年中的第一天作为Date对象。
  • 从中减去一年中的第一天,date然后除以每一天的毫秒数,得到结果。
  • 用于Math.floor()将生成的天数适当舍入为整数。
const dayOfYear = date =>
  Math.floor((date - new Date(date.getFullYear(), 0, 0)) / 1000 / 60 / 60 / 24);
dayOfYear(new Date()); // 272

几天前

计算从今天开始的 n 天前的日期作为字符串表示形式。
  • 使用Date构造函数获取当前日期。
  • 使用Math.abs()andDate.prototype.getDate()相应地更新日期并使用 设置为结果Date.prototype.setDate()
  • 用于Date.prototype.toISOString()以格式返回字符串yyyy-mm-dd
const daysAgo = n => {
  let d = new Date();
  d.setDate(d.getDate() - Math.abs(n));
  return d.toISOString().split('T')[0];
};
daysAgo(20); // 2020-09-16 (if current date is 2020-10-06)

几天后

计算从今天起 n 天的日期作为字符串表示形式。
  • 使用Date构造函数获取当前日期。
  • 使用Math.abs()andDate.prototype.getDate()相应地更新日期并使用 设置为结果Date.prototype.setDate()
  • 用于Date.prototype.toISOString()以格式返回字符串yyyy-mm-dd
const daysFromNow = n => {
  let d = new Date();
  d.setDate(d.getDate() + Math.abs(n));
  return d.toISOString().split('T')[0];
};
daysFromNow(5); // 2020-10-13 (if current date is 2020-10-08)

一个月中的天数

month获取指定 的给定天数year

  • 使用构造函数从给定的和Date创建一个日期。year`month`
  • 将 days 参数设置为0以获取上个月的最后一天,因为月份是零索引的。
  • 用于Date.prototype.getDate()返回给定的天数month
const daysInMonth = (year, month) => new Date(year, month, 0).getDate();
daysInMonth(2020, 12)); // 31
daysInMonth(2024, 2)); // 29

去抖功能(防抖)

创建一个去抖函数,该函数延迟调用提供的函数,直到自上次调用以来至少经过 ms 毫秒。
  • 每次调用 debounced 函数时,使用 clearTimeout() 清除当前挂起的超时。 使用 setTimeout() 创建一个新的超时,延迟调用该函数,直到至少 ms 毫秒已经过去。
  • 使用 Function.prototype.apply() 将 this 上下文应用于函数并提供必要的参数。
  • 省略第二个参数 ms,将超时设置为默认值 0 毫秒。
const debounce = (fn, ms = 0) => {
  let timeoutId;
  return function(...args) {
    clearTimeout(timeoutId);
    timeoutId = setTimeout(() => fn.apply(this, args), ms);
  };
};
window.addEventListener(
  'resize',
  debounce(() => {
    console.log(window.innerWidth);
    console.log(window.innerHeight);
  }, 250)
); // 最多每 250 毫秒记录一次窗口尺寸

防抖promise

创建一个返回`promise`的去抖动(防抖)函数,但延迟调用提供的函数,直到自上次调用它以来至少经过 ms 毫秒。 在此期间返回的所有`promise`将返回相同的数据。
  • 每次调用 debounced 函数时,使用 clearTimeout() 清除当前挂起的超时并使用 setTimeout() 创建一个新的超时来延迟调用该函数,直到至少 ms 毫秒已经过去。
  • 使用 Function.prototype.apply() 将 this 上下文应用于函数并提供必要的参数。
  • 创建一个新的 Promise 并将其 resolve 和 reject 回调添加到待处理的 promises 堆栈。
  • 调用 setTimeout() 时,复制当前堆栈(因为它可以在提供的函数调用及其解析之间更改),清除它并调用提供的函数。
  • 当提供的函数解析/拒绝时,使用返回的数据解析/拒绝堆栈中的所有承诺(调用函数时复制)。
  • 省略第二个参数 ms,将超时设置为默认值 0 毫秒。
const debouncePromise = (fn, ms = 0) => {
  let timeoutId;
  const pending = [];
  return (...args) =>
    new Promise((res, rej) => {
      clearTimeout(timeoutId);
      timeoutId = setTimeout(() => {
        const currentPending = [...pending];
        pending.length = 0;
        Promise.resolve(fn.apply(this, args)).then(
          data => {
            currentPending.forEach(({ resolve }) => resolve(data));
          },
          error => {
            currentPending.forEach(({ reject }) => reject(error));
          }
        );
      }, ms);
      pending.push({ resolve: res, reject: rej });
    });
};
const fn = arg => new Promise(resolve => {
  setTimeout(resolve, 1000, ['resolved', arg]);
});
const debounced = debouncePromise(fn, 200);
debounced('foo').then(console.log);
debounced('bar').then(console.log);
// 将记录 ['resolved', 'bar'] 两次

大写字符串

将字符串的第一个字母取消大写。

  • 使用数组解构并将String.prototype.toLowerCase()第一个字母取消大写,...rest在第一个字母之后获取字符数组,然后Array.prototype.join()再次使其成为一个字符串。
  • 省略upperRest参数以保持字符串的其余部分完整,或将其设置true为转换为大写。
const decapitalize = ([first, ...rest], upperRest = false) =>
  first.toLowerCase() +
  (upperRest ? rest.join('').toUpperCase() : rest.join(''));

decapitalize('FooBar'); // 'fooBar'
decapitalize('FooBar', true); // 'fOOBAR'

深度克隆对象(深拷贝)

创建对象的深度克隆。克隆基元、数组和对象,不包括类实例。

  • 使用递归。
  • 检查传递的对象是否是null,如果是,则返回null
  • 使用Object.assign()和一个空对象 ( {}) 创建原始对象的浅克隆。
  • 使用Object.keys()andArray.prototype.forEach()来确定需要深度克隆哪些键值对。
  • 如果对象是Array,则将 的设置为原始对象的clone对象并用于创建克隆。length`Array.from()`
const deepClone = obj => {
  if (obj === null) return null;
  let clone = Object.assign({}, obj);
  Object.keys(clone).forEach(
    key =>
      (clone[key] =
        typeof obj[key] === 'object' ? deepClone(obj[key]) : obj[key])
  );
  if (Array.isArray(obj)) {
    clone.length = obj.length;
    return Array.from(clone);
  }
  return clone;
};
const a = { foo: 'bar', obj: { a: 1, b: 2 } };
const b = deepClone(a); // a !== b, a.obj !== b.obj

数组扁平化

深度展平数组。

  • 使用递归。
  • Array.prototype.concat()与空数组 ( []) 和展开运算符 ( ) 一起使用...以展平数组。
  • 递归地展平数组中的每个元素。
const deepFlatten = arr =>
  [].concat(...arr.map(v => (Array.isArray(v) ? deepFlatten(v) : v)));

deepFlatten([1, [2], [[3], 4], 5]); // [1, 2, 3, 4, 5]

深度冻结对象

深度冻结对象。

  • 用于Object.keys()获取传递对象的所有属性,Array.prototype.forEach()以迭代它们。
  • 递归Object.freeze()调用所有属性,deepFreeze()必要时应用。
  • 最后,使用Object.freeze()冻结给定的对象。
const deepFreeze = obj => {
  Object.keys(obj).forEach(prop => {
    if (typeof obj[prop] === 'object') deepFreeze(obj[prop]);
  });
  return Object.freeze(obj);
};
'use strict';

const val = deepFreeze([1, [2, 3]]);

val[0] = 3; // 不允许
val[1][0] = 4; // 也不允许

根据键数组获取对象中的嵌套值(深度获取)

基于数组获取嵌套 JSON 对象中的目标值keys

  • 将嵌套 JSON 对象中所需的键作为Array.
  • 用于Array.prototype.reduce()逐个获取嵌套的 JSON 对象中的值。
  • 如果对象中存在键,则返回目标值,否则返回null
const deepGet = (obj, keys) =>
  keys.reduce(
    (xs, x) => (xs && xs[x] !== null && xs[x] !== undefined ? xs[x] : null),
    obj
  );
let index = 2;
const data = {
  foo: {
    foz: [1, 2, 3],
    bar: {
      baz: ['a', 'b', 'c']
    }
  }
};
deepGet(data, ['foo', 'foz', index]); // get 3
deepGet(data, ['foo', 'bar', 'baz', 8, 'foz']); // null

深度map对象键

深度映射对象的键。

  • 创建一个对象,其值与提供的对象和通过为每个键运行提供的函数生成的键相同。
  • 用于Object.keys()迭代对象的键。
  • 用于Array.prototype.reduce()创建具有相同值和映射键的新对象fn
const deepMapKeys = (obj, fn) =>
  Array.isArray(obj)
    ? obj.map(val => deepMapKeys(val, fn))
    : typeof obj === 'object'
    ? Object.keys(obj).reduce((acc, current) => {
        const key = fn(current);
        const val = obj[current];
        acc[key] =
          val !== null && typeof val === 'object' ? deepMapKeys(val, fn) : val;
        return acc;
      }, {})
    : obj;
const obj = {
  foo: '1',
  nested: {
    child: {
      withArray: [
        {
          grandChild: ['hello']
        }
      ]
    }
  }
};
const upperKeysObj = deepMapKeys(obj, key => key.toUpperCase());
/*
{
  "FOO":"1",
  "NESTED":{
    "CHILD":{
      "WITHARRAY":[
        {
          "GRANDCHILD":[ 'hello' ]
        }
      ]
    }
  }
}
*/

深度合并对象

深度合并两个对象,使用一个函数来处理两个对象中存在的键。

  • 用于Object.keys()获取两个对象的键,Set从它们创建一个并使用扩展运算符 ( ...) 创建所有唯一键的数组。
  • 用于Array.prototype.reduce()将每个唯一键添加到对象,用于fn组合两个给定对象的值。
const deepMerge = (a, b, fn) =>
  [...new Set([...Object.keys(a), ...Object.keys(b)])].reduce(
    (acc, key) => ({ ...acc, [key]: fn(key, a[key], b[key]) }),
    {}
  );

deepMerge(
  { a: true, b: { c: [1, 2, 3] } },
  { a: false, b: { d: [1, 2, 3] } },
  (key, a, b) => (key === 'a' ? a && b : Object.assign({}, a, b))
);
// { a: false, b: { c: [ 1, 2, 3 ], d: [ 1, 2, 3 ] } }

为对象属性分配默认值

为对象中的所有属性分配默认值undefined

  • 用于Object.assign()创建一个新的空对象并复制原始对象以保持键顺序。
  • 使用Array.prototype.reverse()和扩展运算符 ( ...) 从左到右组合默认值。
  • 最后,obj再次使用覆盖原来有值的属性。
const defaults = (obj, ...defs) =>
  Object.assign({}, obj, ...defs.reverse(), obj);
defaults({ a: 1 }, { b: 2 }, { b: 6 }, { a: 3 }); // { a: 1, b: 2 }

延迟函数调用

推迟调用函数,直到当前调用堆栈已清除。

  • 使用mssetTimeout()超时1将新事件添加到事件队列并允许渲染引擎完成其工作。
  • 使用扩展 ( ...) 运算符为函数提供任意数量的参数。
const defer = (fn, ...args) => setTimeout(fn, 1, ...args);
// Example A:
defer(console.log, 'a'), console.log('b'); // logs 'b' then 'a'

// Example B:
document.querySelector('#someElement').innerHTML = 'Hello';
longRunningFunction();
// 在完成之前浏览器不会更新 HTML
defer(longRunningFunction);
// 浏览器将更新 HTML 然后运行函数

度数转弧度

将角度从度数转换为弧度。

  • 使用Math.PI度数转弧度公式将角度从度数转换为弧度。
const degreesToRads = deg => (deg * Math.PI) / 180.0;

degreesToRads(90.0); // ~1.5708

延迟函数执行

在 ms 毫秒后调用提供的函数。

  • 用于setTimeout()延迟执行fn.
  • 使用扩展 ( ...) 运算符为函数提供任意数量的参数。
const delay = (fn, ms, ...args) => setTimeout(fn, ms, ...args);
delay(
  function(text) {
    console.log(text);
  },
  1000,
  'later'
); // 一秒钟后'later'记录。

检测设备类型

检测页面是在移动设备上还是在桌面上查看。

  • 使用正则表达式测试Navigator.userAgent属性以确定设备是移动设备还是桌面设备。
const detectDeviceType = () =>
  /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
    navigator.userAgent
  )
    ? 'Mobile'
    : 'Desktop';
detectDeviceType(); // 'Mobile' or 'Desktop'

检测语言

检测当前用户的首选语言。

  • 如果可用,则使用Navigator.language或 的第一个值Navigator.languages,否则返回defaultLang
  • 省略第二个参数,defaultLang用作'en-US'默认语言代码。
const detectLanguage = (defaultLang = 'en-US') =>
  navigator.language ||
  (Array.isArray(navigator.languages) && navigator.languages[0]) ||
  defaultLang;
detectLanguage(); // 'nl-NL'

数组差异

计算两个数组之间的差异,不过滤重复值。

  • 创建一个Setfromb以获取 中的唯一值b
  • 使用Array.prototype.filter()ona仅保留不包含在b, using中的值Set.prototype.has()
const difference = (a, b) => {
  const s = new Set(b);
  return a.filter(x => !s.has(x));
};
difference([1, 2, 3, 3], [1, 2, 4]); // [3, 3]

映射数组差异

在将提供的函数应用于两个数组的每个数组元素之后,返回两个数组之间的差异。

  • 通过将 fn 应用于 b 中的每个元素来创建一个 Set
  • 使用 Array.prototype.map() 将 fn 应用于 a 中的每个元素。。
  • 将 Array.prototype.filter() 与 fn 结合使用在 a 上,使用 Set.prototype.has() 仅保留 b 中不包含的值。
const differenceBy = (a, b, fn) => {
  const s = new Set(b.map(fn));
  return a.map(fn).filter(el => !s.has(el));
};
differenceBy([2.1, 1.2], [2.3, 3.4], Math.floor); // [1]
differenceBy([{ x: 2 }, { x: 1 }], [{ x: 1 }], v => v.x); // [2]

基于函数的数组差异

从比较器函数不返回 true 的数组中过滤掉所有值。

  • 使用Array.prototype.filter()Array.prototype.findIndex()找到合适的值。
  • 省略最后一个参数 comp,以使用默认的严格相等比较器。
const differenceWith = (arr, val, comp = (a, b) => a === b) =>
  arr.filter(a => val.findIndex(b => comp(a, b)) === -1);
differenceWith(
  [1, 1.2, 1.5, 3, 0],
  [1.9, 3, 0],
  (a, b) => Math.round(a) === Math.round(b)
); // [1, 1.2]
differenceWith([1, 1.2, 1.3], [1, 1.3, 1.5]); // [1.2]

获取对象中的嵌套值

根据给定键获取嵌套 JSON 对象中的目标值。

  • 使用in运算符检查是否target存在于obj.
  • 如果找到,返回 的值obj[target]
  • 否则使用Object.values()andArray.prototype.reduce()递归调用dig每个嵌套对象,直到找到第一个匹配的键/值对。
const dig = (obj, target) =>
  target in obj
    ? obj[target]
    : Object.values(obj).reduce((acc, val) => {
        if (acc !== undefined) return acc;
        if (typeof val === 'object') return dig(val, target);
      }, undefined);
const data = {
  level1: {
    level2: {
      level3: 'some data'
    }
  }
};
dig(data, 'level3'); // 'some data'
dig(data, 'level4'); // undefined

数组化号码

将数字转换为数字数组,必要时删除其符号。

  • 用于Math.abs()去除数字的符号。
  • 将数字转换为字符串,使用扩展运算符 ( ...) 构建数组。
  • 使用Array.prototype.map()andparseInt()将每个值转换为整数。
const digitize = n => [...`${Math.abs(n)}`].map(i => parseInt(i));
digitize(123); // [1, 2, 3]
digitize(-123); // [1, 2, 3]

两点之间的距离

计算两点之间的距离。

  • 用于Math.hypot()计算两点之间的欧氏距离。
const distance = (x0, y0, x1, y1) => Math.hypot(x1 - x0, y1 - y0);

distance(1, 1, 2, 3); // ~2.2361

除法的商和模

返回由给定数字的商和余数组成的数组。

  • 用于Math.floor()获取除法的商x / y
  • 使用模运算符 ( %) 获取除法的余数x / y
const divmod = (x, y) => [Math.floor(x / y), x % y];
divmod(8, 3); // [2, 2]
divmod(3, 8); // [0, 3]
divmod(5, 5); // [1, 0]

从左侧删除列表元素

创建一个新数组,其中的n元素从左侧移除。

  • 用于Array.prototype.slice()从左边移除指定数量的元素。
  • 省略最后一个参数 ,n以使用默认值1
const drop = (arr, n = 1) => arr.slice(n);
drop([1, 2, 3]); // [2, 3]
drop([1, 2, 3], 2); // [3]
drop([1, 2, 3], 42); // []

从右边删除列表元素

创建一个新数组,其中的n元素从右侧移除。

  • 用于Array.prototype.slice()从右边移除指定数量的元素。
  • 省略最后一个参数 ,n以使用默认值1
const dropRight = (arr, n = 1) => arr.slice(0, -n);
dropRight([1, 2, 3]); // [1, 2]
dropRight([1, 2, 3], 2); // [1]
dropRight([1, 2, 3], 42); // []

根据功能从右边删除列表元素

从数组末尾移除元素,直到传递的函数返回true。返回数组中的剩余元素。

  • 遍历数组,使用Array.prototype.slice()to 删除数组的最后一个元素,直到从 返回的值functrue
  • 返回剩余的元素。
const dropRightWhile = (arr, func) => {
  let rightIndex = arr.length;
  while (rightIndex-- && !func(arr[rightIndex]));
  return arr.slice(0, rightIndex + 1);
};
dropRightWhile([1, 2, 3, 4], n => n < 3); // [1, 2]

根据功能从左侧删除列表元素

删除数组中的元素,直到传递的函数返回true。返回数组中的剩余元素。

  • 遍历数组,使用Array.prototype.slice()to 删除数组的第一个元素,直到返回的值functrue
  • 返回剩余的元素。
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]

逻辑或函数

检查是否至少有一个函数返回true一组给定的参数。

  • ||在使用提供的 调用这两个函数的结果上使用逻辑或 ( ) 运算符args
const either = (f, g) => (...args) => f(...args) || g(...args);
const isEven = num => num % 2 === 0;
const isPositive = num => num > 0;
const isPositiveOrEven = either(isPositive, isEven);
isPositiveOrEven(4); // true
isPositiveOrEven(3); // true

dom元素包含另一个元素

检查parent元素是否包含元素child

  • 检查parent不是与 相同的元素child
  • 用于Node.contains()检查parent元素是否包含元素child
const elementContains = (parent, child) =>
  parent !== child && parent.contains(child);
elementContains(
  document.querySelector('head'),
  document.querySelector('title')
);
// true
elementContains(document.querySelector('body'), document.querySelector('body'));
// false

元素被聚焦(元素获取焦点)

检查给定元素是否获得焦点。

  • 用于Document.activeElement确定给定元素是否获得焦点。
const elementIsFocused = el => (el === document.activeElement);
elementIsFocused(el); // 如果元素获得焦点则为真

元素在视口中可见

检查指定的元素在视口中是否可见。

  • 使用Element.getBoundingClientRect(),Window.innerWidthWindow.innerHeight确定给定元素在视口中是否可见。
  • 省略第二个参数以确定元素是否完全可见,或指定true以确定它是否部分可见。
const elementIsVisibleInViewport = (el, partiallyVisible = false) => {
  const { top, left, bottom, right } = el.getBoundingClientRect();
  const { innerHeight, innerWidth } = window;
  return partiallyVisible
    ? ((top > 0 && top < innerHeight) ||
        (bottom > 0 && bottom < innerHeight)) &&
        ((left > 0 && left < innerWidth) || (right > 0 && right < innerWidth))
    : top >= 0 && left >= 0 && bottom <= innerHeight && right <= innerWidth;
};

// e.g. 例如 100x100 视口和 10x10px 元素位于位置 {top: -1, left: 0, bottom: 9, right: 10}
elementIsVisibleInViewport(el); // false - (不完全可见)
elementIsVisibleInViewport(el, true); // true - (部分可见)

字符串以子字符串结尾

检查给定字符串是否以另一个字符串的子字符串结尾。

  • 使用for...in循环并从末尾开始String.prototype.slice()获取给定的每个子字符串。word
  • 用于String.prototype.endsWith()根据text.
  • 如果找到,则返回匹配的子字符串。否则,返回undefined
const endsWithSubstring = (text, word) => {
  for (let i in word) {
    const substr = word.slice(0, i + 1);
    if (text.endsWith(substr)) return substr;
  }
  return undefined;
};
endsWithSubstring('Lorem ipsum dolor sit amet<br /', '<br />'); // '<br /'

检查对象是否相等

在两个值之间执行深度比较以确定它们是否相等。

  • 检查这两个值是否相同。
  • 检查两个值是否同时是Date对象,使用Date.prototype.getTime().
  • 检查这两个值是否都是具有等效值(严格比较)的非对象值。
  • 检查是否只有一个值nullundefined它们的原型是否不同。
  • 如果以上条件均不满足,则使用Object.keys()检查两个值是否具有相同数量的键。
  • 用于通过递归调用检查中Array.prototype.every()的每个键是否a存在b以及它们是否等效equals()
const equals = (a, b) => {
  if (a === b) return true;

  if (a instanceof Date && b instanceof Date)
    return a.getTime() === b.getTime();

  if (!a || !b || (typeof a !== 'object' && typeof b !== 'object'))
    return a === b;

  if (a.prototype !== b.prototype) return false;

  const keys = Object.keys(a);
  if (keys.length !== Object.keys(b).length) return false;

  return keys.every(k => equals(a[k], b[k]));
};
equals(
  { a: [2, { e: 3 }], b: [4], c: 'foo' },
  { a: [2, { e: 3 }], b: [4], c: 'foo' }
); // true
equals([1, 2, 3], { 0: 1, 1: 2, 2: 3 }); // true

转义 HTML

转义用于 HTML 的字符串。

  • String.prototype.replace()与匹配需要转义的字符的正则表达式一起使用。
  • 使用回调函数使用字典对象将每个字符实例替换为其关联的转义字符。
const escapeHTML = str =>
  str.replace(
    /[&<>'"]/g,
    tag =>
      ({
        '&': '&amp;',
        '<': '&lt;',
        '>': '&gt;',
        "'": '&#39;',
        '"': '&quot;'
      }[tag] || tag)
  );
escapeHTML('<a href="#">Me & you</a>');
// '&lt;a href=&quot;#&quot;&gt;Me &amp; you&lt;/a&gt;'

转义正则表达式

转义字符串以在正则表达式中使用。

  • 用于String.prototype.replace()转义特殊字符。
const escapeRegExp = str => str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
escapeRegExp('(test)'); // \\(test\\)

欧氏距离

计算任意维数中两点之间的距离。

  • 使用Object.keys()Array.prototype.map()将每个坐标映射到两点之间的差值。
  • 用于Math.hypot()计算两点之间的欧氏距离。
const euclideanDistance = (a, b) =>
  Math.hypot(...Object.keys(a).map(k => b[k] - a[k]));

euclideanDistance([1, 1], [2, 3]); // ~2.2361
euclideanDistance([1, 1, 1], [2, 3, 2]); // ~2.4495

每n个元素

返回数组中的每第 n 个元素。

  • 用于创建包含给定数组的Array.prototype.filter()每个元素的新数组。nth
const everyNth = (arr, nth) => arr.filter((e, i) => i % nth === nth - 1);
everyNth([1, 2, 3, 4, 5, 6], 2); // [ 2, 4, 6 ]

将制表符扩展为空格

将制表符转换为空格,其中每个制表符对应一个count空格。

  • String.prototype.replace()与正则表达式一起使用并String.prototype.repeat()count空格替换每个制表符。
const expandTabs = (str, count) => str.replace(/\t/g, ' '.repeat(count));
expandTabs('\t\tlorem', 3); // '      lorem'

3 位颜色代码扩展为 6 位颜色代码。

将 3 位颜色代码扩展为 6 位颜色代码。

  • 使用Array.prototype.map(),String.prototype.split()Array.prototype.join()连接映射数组,将 3 位 RGB 表示的十六进制颜色代码转换为 6 位形式。
  • Array.prototype.slice()用于#从字符串开始删除,因为它被添加了一次。
const extendHex = shortHex =>
  '#' +
  shortHex
    .slice(shortHex.startsWith('#') ? 1 : 0)
    .split('')
    .map(x => x + x)
    .join('');
extendHex('#03f'); // '#0033ff'
extendHex('05a'); // '#0055aa'

数的阶乘

计算一个数的阶乘。

  • 使用递归。
  • 如果n小于或等于1,则返回1
  • 否则,返回 的乘积n和 的阶乘n - 1
  • TypeError如果n是负数则抛出一个。
const factorial = n =>
  n < 0
    ? (() => {
        throw new TypeError('Negative numbers are not allowed!');
      })()
    : n <= 1
    ? 1
    : n * factorial(n - 1);
factorial(6); // 720

华氏度到摄氏度

将华氏度转换为摄氏度。

  • 遵循转换公式C = (F - 32) * 5 / 9
const fahrenheitToCelsius = degrees => (degrees - 32) * 5 / 9;
fahrenheitToCelsius(32); // 0

斐波那契数列

生成一个包含斐波那契数列的数组,直到第 n 个项。

  • 用于Array.from()创建特定长度的空数组,初始化前两个值 (01)。
  • 使用Array.prototype.reduce()andArray.prototype.concat()将值添加到数组中,使用除前两个值之外的最后两个值的总和。
const fibonacci = n =>
  Array.from({ length: n }).reduce(
    (acc, val, i) => acc.concat(i > 1 ? acc[i - 1] + acc[i - 2] : i),
    []
  );
fibonacci(6); // [0, 1, 1, 2, 3, 5]

过滤非唯一数组值

创建一个过滤掉非唯一值的数组。

  • 使用Set构造函数和展开运算符 ( ...) 创建 中唯一值的数组arr
  • 用于Array.prototype.filter()创建仅包含唯一值的数组。
const filterNonUnique = arr =>
  [...new Set(arr)].filter(i => arr.indexOf(i) === arr.lastIndexOf(i));
filterNonUnique([1, 2, 2, 3, 4, 4, 5]); // [1, 3, 5]

根据函数过滤非唯一数组值

根据提供的比较器函数,创建一个过滤掉非唯一值的数组。

  • 基于比较器函数,使用Array.prototype.filter()andArray.prototype.every()创建仅包含唯一值的数组fn
  • 比较器函数有四个参数:被比较的两个元素的值和它们的索引。
const filterNonUniqueBy = (arr, fn) =>
  arr.filter((v, i) => arr.every((x, j) => (i === j) === fn(v, x, i, j)));
filterNonUniqueBy(
  [
    { id: 0, value: 'a' },
    { id: 1, value: 'b' },
    { id: 2, value: 'c' },
    { id: 1, value: 'd' },
    { id: 0, value: 'e' }
  ],
  (a, b) => a.id === b.id
); // [ { id: 2, value: 'c' } ]

过滤唯一数组值

创建一个过滤掉唯一值的数组。

  • 使用Set构造函数和展开运算符 ( ...) 创建 中唯一值的数组arr
  • 用于Array.prototype.filter()创建仅包含非唯一值的数组。
const filterUnique = arr =>
  [...new Set(arr)].filter(i => arr.indexOf(i) !== arr.lastIndexOf(i));
filterUnique([1, 2, 2, 3, 4, 4, 5]); // [2, 4]

根据函数过滤唯一数组值

根据提供的比较器函数,创建一个过滤掉唯一值的数组。

  • 基于比较器函数,使用Array.prototype.filter()andArray.prototype.every()创建仅包含非唯一值的数组fn
  • 比较器函数有四个参数:被比较的两个元素的值和它们的索引。
const filterUniqueBy = (arr, fn) =>
  arr.filter((v, i) => arr.some((x, j) => (i !== j) === fn(v, x, i, j)));

filterUniqueBy(
  [
    { id: 0, value: 'a' },
    { id: 1, value: 'b' },
    { id: 2, value: 'c' },
    { id: 3, value: 'd' },
    { id: 0, value: 'e' }
  ],
  (a, b) => a.id == b.id
); // [ { id: 0, value: 'a' }, { id: 0, value: 'e' } ]

找到最近的锚点

查找最接近给定节点的锚节点(如果有)。

  • 使用for循环 andNode.parentNode从给定的 向上遍历节点树node
  • 使用Node.nodeNameandString.prototype.toLowerCase()检查任何给定节点是否为锚点 ( 'a')。
  • 如果没有找到匹配的节点,则返回null

const findClosestAnchor = node => {
  for (let n = node; n.parentNode; n = n.parentNode)
    if (n.nodeName.toLowerCase() === 'a') return n;
  return null;
};

findClosestAnchor(document.querySelector('a > span')); // a

找到最接近的匹配节点

从给定节点开始查找最接近的匹配节点。

  • 使用for循环 andNode.parentNode从给定的 向上遍历节点树node
  • 用于Element.matches()检查任何给定的元素节点是否与提供的匹配selector
  • 如果没有找到匹配的节点,则返回null
const findClosestMatchingNode = (node, selector) => {
  for (let n = node; n.parentNode; n = n.parentNode)
    if (n.matches && n.matches(selector)) return n;
  return null;
};
findClosestMatchingNode(document.querySelector('span'), 'body'); // body

连续元素数组

查找所有连续元素的数组。

  • 用于Array.prototype.slice()创建一个数组,其中的n - 1元素从一开始就被移除。
  • 使用Array.prototype.map()andArray.prototype.slice()将每个元素映射到连续元素数组n
const findConsecutive = (arr, n) =>
  arr.slice(n - 1).map((v, i) => arr.slice(i, i + n));
findConsecutive([1, 2, 3, 4, 5], 2);
// [[1, 2], [2, 3], [3, 4], [4, 5]]

查找前 n 个匹配项

查找提供的函数为其返回真值的前 n 个元素。
  • 使用 for...in 循环为 arr 的每个元素执行提供的匹配器。
  • 使用 Array.prototype.push() 将元素追加到结果数组并在其长度等于 n 时返回它们。
const findFirstN = (arr, matcher, n = 1) => {
  let res = [];
  for (let i in arr) {
    const el = arr[i];
    const match = matcher(el, i, arr);
    if (match) res.push(el);
    if (res.length === n) return res;
  }
  return res;
};
findFirstN([1, 2, 4, 6], n => n % 2 === 0, 2); // [2, 4]
findFirstN([1, 2, 4, 6], n => n % 2 === 0, 5); // [2, 4, 6]

找到第一个匹配的键

查找满足提供的测试功能的第一个键。否则undefined退回。

  • 用于Object.keys()获取对象的所有属性,Array.prototype.find()使用 来测试每个键值对fn
  • 回调接收三个参数——值、键和对象。
const findKey = (obj, fn) =>
  Object.keys(obj).find(key => fn(obj[key], key, obj));
findKey(
  {
    barney: { age: 36, active: true },
    fred: { age: 40, active: false },
    pebbles: { age: 1, active: true }
  },
  x => x['active']
); // 'barney'

找到匹配的键

在提供的对象中查找与给定值匹配的所有键。

  • 用于Object.keys()获取对象的所有属性。
  • 用于Array.prototype.filter()测试每个键值对并返回所有等于给定值的键。
const findKeys = (obj, val) =>
  Object.keys(obj).filter(key => obj[key] === val);

const ages = {
  Leo: 20,
  Zoey: 21,
  Jane: 20,
};
findKeys(ages, 20); // [ 'Leo', 'Jane' ]

找到最后一个匹配值

查找提供的函数为其返回真值的最后一个元素。

  • 使用 Array.prototype.filter() 删除 fn 返回虚假值的元素。
  • 使用 Array.prototype.pop() 获取过滤后数组中的最后一个元素。
const findLast = (arr, fn) => arr.filter(fn).pop();
findLast([1, 2, 3, 4], n => n % 2 === 1); // 3

查找最后匹配的索引

查找提供的函数为其返回真值的最后一个元素的索引。

  • 用于Array.prototype.map()将每个元素映射到具有其索引和值的数组。
  • 用于删除返回虚假值Array.prototype.filter()的元素fn
  • 用于Array.prototype.pop()获取筛选数组中的最后一个元素。
  • -1如果没有匹配的元素则返回。
const findLastIndex = (arr, fn) =>
  (arr
    .map((val, i) => [i, val])
    .filter(([i, val]) => fn(val, i, arr))
    .pop() || [-1])[0];
findLastIndex([1, 2, 3, 4], n => n % 2 === 1); // 2 (值 3 的索引)
findLastIndex([1, 2, 3, 4], n => n === 5); // -1 (找不到时的默认值)

查找最后匹配的键

查找满足提供的测试功能的最后一个键。否则undefined退回。

  • 用于Object.keys()获取对象的所有属性。
  • 用于Array.prototype.reverse()反转顺序并Array.prototype.find()测试为每个键值对提供的函数。
  • 回调接收三个参数——值、键和对象。
const findLastKey = (obj, fn) =>
  Object.keys(obj)
    .reverse()
    .find(key => fn(obj[key], key, obj));
findLastKey(
  {
    barney: { age: 36, active: true },
    fred: { age: 40, active: false },
    pebbles: { age: 1, active: true }
  },
  x => x['active']
); // 'pebbles'

查找最后 n 个匹配项

查找提供的函数为其返回真值的最后 n 个元素。
  • 使用 for 循环为 arr 的每个元素执行提供的匹配器。
  • 使用 Array.prototype.unshift() 将元素添加到结果数组并在其长度等于 n 时返回它们。
const findLastN = (arr, matcher, n = 1) => {
  let res = [];
  for (let i = arr.length - 1; i >= 0; i--) {
    const el = arr[i];
    const match = matcher(el, i, arr);
    if (match) res.unshift(el);
    if (res.length === n) return res;
  }
  return res;
};
findLastN([1, 2, 4, 6], n => n % 2 === 0, 2); // [4, 6]
findLastN([1, 2, 4, 6], n => n % 2 === 0, 5); // [2, 4, 6]

前 n 个元素

获取数组的前 n 个元素。

  • 使用起始值为0且结束值为 n 的 Array.prototype.slice() 来获取 arr 的前 n 个元素。
const firstN = (arr, n) => arr.slice(0, n);
firstN(['a', 'b', 'c', 'd'], 2); // ['a', 'b']

数组转换为标记对象

将字符串数组转换为对象值到 true。

  • 使用 Array.prototype.reduce() 将数组转换为对象,其中每个数组值用作其值设置为 true 的键。
const flags = arr => arr.reduce((acc, str) => ({...acc, [str]: true }), {});
flags(['red', 'green']); // { red: true, green: true }

平面迭代器(数组扁平化)

创建一个生成器,它迭代一个可迭代的、展平嵌套的可迭代对象。

  • 使用递归。
  • 使用for...of循环迭代给定可迭代对象的值。
  • 用于Symbol.iterator检查每个值是否是可迭代的。如果是,则使用yield*表达式递归委托给同一个生成器函数。否则,yield当前值。
const flatIterator = function* (itr) {
  for (let item of itr) {
    if (item[Symbol.iterator]) yield* flatIterator(item);
    else yield item;
  }
};
const arr = [1, 2, [3, 4], [5, [6, [7], 8]], 9, new Set([10, 11])];
[...flatIterator(arr)]; // 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11

展平数组

将数组展平到指定深度。

  • 使用递归,递减depth每个1深度级别。
  • 使用Array.prototype.reduce()andArray.prototype.concat()合并元素或数组。
  • 基本情况,等于depth停止1递归。
  • 省略第二个参数 ,depth仅展平到深度1(single 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]

展平对象(对象扁平化)

使用键的路径展平对象。

  • 使用递归。
  • 使用 Object.keys() 结合 Array.prototype.reduce() 将每个叶节点转换为扁平路径节点。
  • 如果键的值是一个对象,则该函数使用适当的前缀调用自身以使用 Object.assign() 创建路径。
  • 否则,它将适当的前缀键值对添加到累加器对象。
  • 你应该总是省略第二个参数,prefix,除非你希望每个键都有一个前缀。
const flattenObject = (obj, prefix = '') =>
  Object.keys(obj).reduce((acc, k) => {
    const pre = prefix.length ? `${prefix}.` : '';
    if (
      typeof obj[k] === 'object' &&
      obj[k] !== null &&
      Object.keys(obj[k]).length > 0
    )
      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 }

翻转函数参数

将函数作为参数,然后将第一个参数作为最后一个参数。

  • 使用参数解构和带有可变参数的闭包。
  • 使用扩展运算符 ( ) 拼接第一个参数...,使其成为最后一个,然后再应用其余参数。
const flip = fn => (first, ...rest) => fn(...rest, first);
let a = { name: 'John Smith' };
let b = {};
const mergeFrom = flip(Object.assign);
let mergePerson = mergeFrom.bind(null, a);
mergePerson(b); // == b
b = {};
Object.assign(b, a); // == b

对每个数组元素反向执行函数

从数组的最后一个元素开始,为每个数组元素执行一次提供的函数。

  • 用于Array.prototype.slice()克隆给定的数组并Array.prototype.reverse()反转它。
  • 用于Array.prototype.forEach()迭代反向数组。
const forEachRight = (arr, callback) =>
  arr
    .slice()
    .reverse()
    .forEach(callback);
forEachRight([1, 2, 3, 4], val => console.log(val)); // '4', '3', '2', '1'

遍历对象自身的属性

遍历一个对象的所有属性,为每个属性运行一个回调。

  • 用于Object.keys()获取对象的所有属性。
  • 用于Array.prototype.forEach()为每个键值对运行提供的函数。
  • 回调接收三个参数——值、键和对象。
const forOwn = (obj, fn) =>
  Object.keys(obj).forEach(key => fn(obj[key], key, obj));
forOwn({ foo: 'bar', a: 1 }, v => console.log(v)); // 'bar', 1

反向迭代对象自身的属性

反向迭代一个对象的所有属性,为每个属性运行一个回调。

  • 用于Object.keys()获取对象的所有属性,Array.prototype.reverse()颠倒它们的顺序。
  • 用于Array.prototype.forEach()为每个键值对运行提供的函数。
  • 回调接收三个参数——值、键和对象。
const forOwnRight = (obj, fn) =>
  Object.keys(obj)
    .reverse()
    .forEach(key => fn(obj[key], key, obj));
forOwnRight({ foo: 'bar', a: 1 }, v => console.log(v)); // 1, 'bar'

表单转对象

将一组表单元素编码为一个对象。

  • 使用 FormData 构造函数将 HTML 表单转换为 FormData,并使用 Array.from() 将其转换为数组。
  • 使用 Array.prototype.reduce() 从数组中收集对象。
const formToObject = form =>
  Array.from(new FormData(form)).reduce(
    (acc, [key, value]) => ({
      ...acc,
      [key]: value
    }),
    {}
  );

formToObject(document.querySelector('#form'));
// { email: '[email protected]', name: 'Test Name' }

格式化时间

返回给定毫秒数的人类可读格式。

  • 将 ms 除以适当的值以获得日、小时、分钟、秒和毫秒的适当值。
  • 使用 Object.entries() 和 Array.prototype.filter() 只保留非零值。
  • 使用 Array.prototype.map() 为每个值创建字符串,适当复数化。
  • 使用 Array.prototype.join() 将值组合成一个字符串。
const formatDuration = ms => {
  if (ms < 0) ms = -ms;
  const time = {
    day: Math.floor(ms / 86400000),
    hour: Math.floor(ms / 3600000) % 24,
    minute: Math.floor(ms / 60000) % 60,
    second: Math.floor(ms / 1000) % 60,
    millisecond: Math.floor(ms) % 1000
  };
  return Object.entries(time)
    .filter(val => val[1] !== 0)
    .map(([key, val]) => `${val} ${key}${val !== 1 ? 's' : ''}`)
    .join(', ');
};
formatDuration(1001); // '1 second, 1 millisecond'
formatDuration(34325055574);
// '397 days, 6 hours, 44 minutes, 15 seconds, 574 milliseconds'

格式化数字

使用本地数字格式顺序格式化数字。

  • 用于Number.prototype.toLocaleString()将数字转换为使用本地数字格式分隔符。
const formatNumber = num => num.toLocaleString();
formatNumber(123456); // '123,456' in `en-US`
formatNumber(15675436903); // '15.675.436.903' in `de-DE`

ISO格式的秒数(格式化秒)

返回给定秒数的 ISO 格式。

  • 将 s 除以适当的值以获得小时、分钟和秒的适当值。
  • 将符号存储在sign变量中以将其添加到结果中。
  • 将 Array.prototype.map() 与 Math.floor() 和 String.prototype.padStart() 结合使用以对每个段进行字符串化和格式化。
  • 使用 Array.prototype.join() 将值组合成一个字符串。
const formatSeconds = s => {
  const [hour, minute, second, sign] =
    s > 0
      ? [s / 3600, (s / 60) % 60, s % 60, '']
      : [-s / 3600, (-s / 60) % 60, -s % 60, '-'];

  return (
    sign +
    [hour, minute, second]
      .map(v => `${Math.floor(v)}`.padStart(2, '0'))
      .join(':')
  );
};
formatSeconds(200); // '00:03:20'
formatSeconds(-200); // '-00:03:20'
formatSeconds(99999); // '27:46:39'

值出现频率

创建一个对象,将数组的唯一值作为键,将它们的频率作为值。

  • 用于Array.prototype.reduce()将唯一值映射到对象的键,每次遇到相同的值时添加到现有键。
const frequencies = arr =>
  arr.reduce((a, v) => {
    a[v] = a[v] ? a[v] + 1 : 1;
    return a;
  }, {});

frequencies(['a', 'b', 'a', 'c', 'a', 'a', 'b']); // { a: 4, b: 2, c: 1 }
frequencies([...'ball']); // { b: 1, a: 1, l: 2 }

驼峰式转换字符串

从驼峰式转换字符串。

  • 用于String.prototype.replace()将字符串分解为单词并separator在它们之间添加一个。
  • 省略第二个参数以使用默认separator_.
const fromCamelCase = (str, separator = '_') =>
  str
    .replace(/([a-z\d])([A-Z])/g, '$1' + separator + '$2')
    .replace(/([A-Z]+)([A-Z][a-z\d]+)/g, '$1' + separator + '$2')
    .toLowerCase();

fromCamelCase('someDatabaseFieldName', ' '); // 'some database field name'
fromCamelCase('someLabelThatNeedsToBeDecamelized', '-');
// 'some-label-that-needs-to-be-decamelized'
fromCamelCase('someJavascriptProperty', '_'); // 'some_javascript_property'
fromCamelCase('JSONToCSV', '.'); // 'json.to.csv'

来自 Unix 时间戳的日期

Date从 Unix 时间戳创建一个对象。

  • 通过乘以 将时间戳转换为毫秒1000
  • 使用Date构造函数创建一个新Date对象。
const fromTimestamp = timestamp => new Date(timestamp * 1000);
fromTimestamp(1602162242); // 2020-10-08T13:04:02.000Z

冻结集合对象

创建冻结Set对象。

  • 使用Set构造函数Setiterable.
  • 将新创建的对象的add,delete和方法设置为, 使其无法使用, 几乎冻结了对象。clear`undefined`
const frozenSet = iterable => {
  const s = new Set(iterable);
  s.add = undefined;
  s.delete = undefined;
  s.clear = undefined;
  return s;
};
frozenSet([1, 2, 3, 1, 2]);
// Set { 1, 2, 3, add: undefined, delete: undefined, clear: undefined }

切换全屏模式

在全屏模式下打开或关闭元素。

  • 使用Document.querySelector()Element.requestFullscreen()在全屏中打开给定的元素。
  • 用于Document.exitFullscreen()退出全屏模式。
  • 省略第二个参数 ,el用作body默认元素。
  • 省略第一个元素,mode默认情况下以全屏模式打开该元素。
const fullscreen = (mode = true, el = 'body') =>
  mode
    ? document.querySelector(el).requestFullscreen()
    : document.exitFullscreen();

fullscreen(); // 以全屏模式打开“body”
fullscreen(false); //退出全屏模式

获取函数名

记录函数的名称。

  • 使用console.debug()name传递的函数的属性将函数的名称记录到debug控制台的通道。
  • 返回给定的函数fn
const functionName = fn => (console.debug(fn.name), fn);

let m = functionName(Math.max)(5, 6);
// max (登录控制台的调试通道)
// m = 6

函数属性名称

从对象自己的(和可选的继承的)可枚举属性中获取函数属性名称的数组。

  • 用于Object.keys()迭代对象自身的属性。
  • 如果inheritedtrueObject.getPrototypeOf()则还用于获取对象的继承属性。
  • 用于Array.prototype.filter()仅保留那些作为函数的属性。
  • 省略第二个参数,inherited默认情况下不包括继承的属性。
const functions = (obj, inherited = false) =>
  (inherited
    ? [...Object.keys(obj), ...Object.keys(Object.getPrototypeOf(obj))]
    : Object.keys(obj)
  ).filter(key => typeof obj[key] === 'function');
function Foo() {
  this.a = () => 1;
  this.b = () => 2;
}
Foo.prototype.c = () => 3;
functions(new Foo()); // ['a', 'b']
functions(new Foo(), true); // ['a', 'b', 'c']

最大公约数

计算两个或多个数字/数组之间的最大公约数。

  • 内部_gcd函数使用递归。
  • 基本情况是当y等于时0。在这种情况下,返回x
  • 否则,返回的 GCDy和除法的余数x / y
const gcd = (...arr) => {
  const _gcd = (x, y) => (!y ? x : gcd(y, x % y));
  return [...arr].reduce((a, b) => _gcd(a, b));
};
gcd(8, 36); // 4
gcd(...[12, 8, 32]); // 4

生成项目(生成数组)

使用给定函数生成具有给定数量项目的数组。

  • 用于 Array.from() 创建特定长度的空数组,调用fn每个新创建元素的索引。
  • 回调接受一个参数——每个元素的索引。
const generateItems = (n, fn) => Array.from({ length: n }, (_, i) => fn(i));
generateItems(10, Math.random);
// [0.21, 0.08, 0.40, 0.96, 0.96, 0.24, 0.19, 0.96, 0.42, 0.70]

生成直到满足条件的数组

创建一个生成器,在满足给定条件之前不断生成新值。

  • 使用seed值初始化当前 val。
  • 当使用当前 val 调用的条件函数返回 false 时,使用 while 循环进行迭代。
  • 使用 yield 返回当前 val 并可选择接收新的种子值 nextSeed。
  • 使用 next 函数从当前 val 和 nextSeed 计算下一个值。
const generateUntil = function* (seed, condition, next) {
  let val = seed;
  let nextSeed = null;
  while (!condition(val)) {
    nextSeed = yield val;
    val = next(val, nextSeed);
  }
  return val;
};
[...generateUntil(1, v => v > 5, v => ++v)]; // [1, 2, 3, 4, 5]

满足条件时生成数组

创建一个生成器,只要满足给定条件,它就会不断生成新值。

  • 使用seed值初始化当前 val。
  • 当使用当前 val 调用的条件函数返回 true 时,使用 while 循环进行迭代。
  • 使用 yield 返回当前 val 并可选择接收新的种子值 nextSeed。
  • 使用 next 函数从当前 val 和 nextSeed 计算下一个值。
const generateWhile = function* (seed, condition, next) {
  let val = seed;
  let nextSeed = null;
  while (condition(val)) {
    nextSeed = yield val;
    val = next(val, nextSeed);
  }
  return val;
};

[...generateWhile(1, v => v <= 5, v => ++v)]; // [1, 2, 3, 4, 5]

生成器到数组

将生成器函数的输出转换为数组。

  • 使用扩展运算符 ( ...) 将生成器函数的输出转换为数组。
const generatorToArray = gen => [...gen];
const s = new Set([1, 2, 1, 3, 1, 4]);
generatorToArray(s.entries()); // [[ 1, 1 ], [ 2, 2 ], [ 3, 3 ], [ 4, 4 ]]

几何级数

初始化一个数组,其中包含指定范围内的数字,其中开始和结束都包含在内,并且两项之间的比率为步长。 如果 step 等于 1,则返回错误。

  • 使用 Array.from()、Math.log() 和 Math.floor() 创建所需长度的数组,使用 Array.prototype.map() 填充范围内的所需值。
  • 省略第二个参数 ,start以使用默认值1
  • 省略第三个参数 ,step以使用默认值2
const geometricProgression = (end, start = 1, step = 2) =>
  Array.from({
    length: Math.floor(Math.log(end / start) / Math.log(step)) + 1,
  }).map((_, i) => start * step ** i);
geometricProgression(256); // [1, 2, 4, 8, 16, 32, 64, 128, 256]
geometricProgression(256, 3); // [3, 6, 12, 24, 48, 96, 192]
geometricProgression(256, 1, 4); // [1, 4, 16, 64, 256]

从路径字符串中获取嵌套对象属性

从对象中检索由给定选择器指示的一组属性。

  • 对每个选择器使用 Array.prototype.map(),使用 String.prototype.replace() 将方括号替换为点。
  • 使用 String.prototype.split() 来拆分每个选择器。
  • 使用 Array.prototype.filter() 删除空值,使用 Array.prototype.reduce() 获取每个选择器指示的值。
const get = (from, ...selectors) =>
  [...selectors].map(s =>
    s
      .replace(/\[([^\[\]]*)\]/g, '.$1.')
      .split('.')
      .filter(t => t !== '')
      .reduce((prev, cur) => prev && prev[cur], from)
  );
const obj = {
  selector: { to: { val: 'val to select' } },
  target: [1, 2, { a: 'test' }],
};
get(obj, 'selector.to.val', 'target[0]', 'target[2].a');
// ['val to select', 1, 'test']

获取dom元素所有祖先

返回从文档根到给定元素的元素的所有祖先。

  • 使用 Node.parentNode 和 while 循环向上移动元素的祖先树。
  • 使用 Array.prototype.unshift() 将每个新祖先添加到数组的开头。
const getAncestors = el => {
  let ancestors = [];
  while (el) {
    ancestors.unshift(el);
    el = el.parentNode;
  }
  return ancestors;
};
getAncestors(document.querySelector('nav'));
// [document, html, body, header, nav]

获取基本网址

获取不带任何参数或片段标识符的当前 URL。

  • 将 String.prototype.replace() 与适当的正则表达式一起使用,以删除“?”或“#”之后的所有内容 (如果找到)。
const getBaseURL = url => url.replace(/[?#].*$/, '');
getBaseURL('http://url.com/page?name=Adam&surname=Smith');
// 'http://url.com/page'

获取命令行参数

获取传递给 Node.js 脚本的命令行参数。

  • 用于process.argv获取所有命令行参数的数组。
  • 使用 Array.prototype.slice() 删除前两个元素(Node.js 可执行文件的路径和正在执行的文件)。
const getCmdArgs = () => process.argv.slice(2);
// node my-script.js --name=John --age=30
getCmdArgs(); // ['--name=John', '--age=30']

从日期获取冒号时间

HH:MM:SS从对象返回一个形式的字符串Date

  • 使用Date.prototype.toTimeString()andString.prototype.slice()获取HH:MM:SS给定Date对象的一部分。
const getColonTimeFromDate = date => date.toTimeString().slice(0, 8);

getColonTimeFromDate(new Date()); // '08:38:00'

以天为单位计算日期

计算两个日期之间的差异(以天为单位)。

  • 将两个Date对象相减并除以一天中的毫秒数以获得它们之间的差异(以天为单位)。
const getDaysDiffBetweenDates = (dateInitial, dateFinal) =>
  (dateFinal - dateInitial) / (1000 * 3600 * 24);
getDaysDiffBetweenDates(new Date('2017-12-13'), new Date('2017-12-22')); // 9

获取大于视口的元素

返回宽度大于视口宽度的 HTML 元素数组。

  • 用于HTMLElement.offsetWidth获取 的宽度Document
  • 使用Array.prototype.filter()结果Document.querySelectorAll()检查文档中所有元素的宽度。
const getElementsBiggerThanViewport = () => {
  const docWidth = document.documentElement.offsetWidth;
  return [...document.querySelectorAll('*')].filter(
    el => el.offsetWidth > docWidth
  );
};

getElementsBiggerThanViewport(); // <div id="ultra-wide-item" />

以小时为单位计算日期

计算两个日期之间的差异(以小时为单位)。

  • 将两个Date对象相减并除以一小时中的毫秒数,得到它们之间的差值(以小时为单位)。
const getHoursDiffBetweenDates = (dateInitial, dateFinal) =>
  (dateFinal - dateInitial) / (1000 * 3600);
getHoursDiffBetweenDates(
  new Date('2021-04-24 10:25:00'),
  new Date('2021-04-25 10:25:00')
); // 24

获取element中的所有图片

从元素中获取所有图像并将它们放入数组中。

  • 用于Element.getElementsByTagName()获取<img>提供的元素内的所有元素。
  • 用于Array.prototype.map()映射src每个元素的每个属性<img>
  • 如果includeDuplicatesfalse,则创建一个新的Set以消除重复项并在散布到数组中后将其返回。
  • 省略第二个参数,includeDuplicates默认情况下丢弃重复项。
const getImages = (el, includeDuplicates = false) => {
  const images = [...el.getElementsByTagName('img')].map(img =>
    img.getAttribute('src')
  );
  return includeDuplicates ? images : [...new Set(images)];
};
getImages(document, true); // ['image1.jpg', 'image2.png', 'image1.png', '...']
getImages(document, false); // ['image1.jpg', 'image2.png', '...']

获取整数的子午线后缀

将整数转换为带后缀的字符串,添加ampm基于其值。

  • 使用模运算符 ( %) 和条件检查将整数转换为带有 meridiem 后缀的字符串化 12 小时格式。
const getMeridiemSuffixOfInteger = num =>
  num === 0 || num === 24
    ? 12 + 'am'
    : num === 12
    ? 12 + 'pm'
    : num < 12
    ? (num % 12) + 'am'
    : (num % 12) + 'pm';
getMeridiemSuffixOfInteger(0); // '12am'
getMeridiemSuffixOfInteger(11); // '11am'
getMeridiemSuffixOfInteger(13); // '1pm'
getMeridiemSuffixOfInteger(25); // '1pm'

以分钟为单位计算日期

计算两个日期之间的差异(以分钟为单位)。

  • 将两个Date对象相减并除以一分钟内的毫秒数,得到它们之间的差值(以分钟为单位)。
const getMinutesDiffBetweenDates = (dateInitial, dateFinal) =>
  (dateFinal - dateInitial) / (1000 * 60);
getMinutesDiffBetweenDates(
  new Date('2021-04-24 01:00:15'),
  new Date('2021-04-24 02:00:15')
); // 60

以月为单位计算日期

计算两个日期之间的差异(以月为单位)。

  • 使用 Date.prototype.getFullYear() 和 Date.prototype.getMonth() 计算两个 Date 对象之间的差异(以月为单位)。
const getMonthsDiffBetweenDates = (dateInitial, dateFinal) =>
  Math.max(
    (dateFinal.getFullYear() - dateInitial.getFullYear()) * 12 +
      dateFinal.getMonth() -
      dateInitial.getMonth(),
    0
  );
getMonthsDiffBetweenDates(new Date('2017-12-13'), new Date('2018-04-29')); // 4

获取父元素直到匹配选择器

查找元素的所有祖先,直到元素与指定的选择器匹配。

  • 使用Node.parentNode一个while循环向上移动元素的祖先树。
  • 用于Array.prototype.unshift()将每个新祖先添加到数组的开头。
  • 用于Element.matches()检查当前元素是否匹配指定的selector.
const getParentsUntil = (el, selector) => {
  let parents = [],
    _el = el.parentNode;
  while (_el && typeof _el.matches === 'function') {
    parents.unshift(_el);
    if (_el.matches(selector)) return parents;
    else _el = _el.parentNode;
  }
  return [];
};
getParentsUntil(document.querySelector('#home-link'), 'header');
// [header, nav, ul, li]

当前页面协议

获取当前页面上使用的协议。

  • 用于获取当前页面的Window.location.protocol协议(http:或)。https:
const getProtocol = () => window.location.protocol;
getProtocol(); // 'https:'

获取滚动位置

返回当前页面的滚动位置。

  • 如果定义了 Window.pageXOffset 和 Window.pageYOffset,则使用它们,否则使用 Element.scrollLeft 和 Element.scrollTop。
  • 省略单个参数 ,el以使用全局Window对象。
const getScrollPosition = (el = window) => ({
  x: el.pageXOffset !== undefined ? el.pageXOffset : el.scrollLeft,
  y: el.pageYOffset !== undefined ? el.pageYOffset : el.scrollTop
});

getScrollPosition(); // {x: 0, y: 200}

获取滚动条宽度

计算窗口垂直滚动条的宽度。

  • 用于Window.innerWidth获取窗口的内部宽度。
  • 用于Element.clientWidth获取元素的内部宽度Document
  • 将这两个值相减得到垂直滚动条的宽度。
const getScrollbarWidth = () =>
  window.innerWidth - document.documentElement.clientWidth;
getScrollbarWidth(); // 15

以秒为单位计算日期

计算两个日期之间的差异(以秒为单位)。

  • 将两个Date对象相减并除以一秒内的毫秒数,得到它们之间的差值(以秒为单位)。
const getSecondsDiffBetweenDates = (dateInitial, dateFinal) =>
  (dateFinal - dateInitial) / 1000;
getSecondsDiffBetweenDates(
  new Date('2020-12-24 00:00:15'),
  new Date('2020-12-24 00:00:17')
); // 2

获取选定的文本

获取当前选定的文本。

  • 使用Window.getSelection()andSelection.toString()获取当前选中的文本。
const getSelectedText = () => window.getSelection().toString();
getSelectedText(); // 'Lorem ipsum'

获取兄弟元素

返回包含给定元素的所有兄弟元素的数组。

  • 使用Node.parentNodeandNode.childNodes获取NodeList元素父元素中包含的所有元素。
  • 使用扩展运算符 (...) 和 Array.prototype.filter() 转换为数组并从中删除给定的元素。
const getSiblings = el =>
  [...el.parentNode.childNodes].filter(node => node !== el);
getSiblings(document.querySelector('head')); // ['body']

获取元素的样式

检索指定元素的 CSS 规则值。

  • 用于Window.getComputedStyle()获取指定元素的 CSS 规则值。
const getStyle = (el, ruleName) => getComputedStyle(el)[ruleName];
getStyle(document.querySelector('p'), 'font-size'); // '16px'

从日期开始的 Unix 时间戳

从对象获取 Unix 时间戳Date

  • 用于Date.prototype.getTime()获取以毫秒为单位的时间戳,除以1000获取以秒为单位的时间戳。
  • 使用 Math.floor() 将生成的时间戳适当舍入为整数。
  • 省略参数 ,date以使用当前日期。
const getTimestamp = (date = new Date()) => Math.floor(date.getTime() / 1000);
getTimestamp(); // 1602162242

获取数据类型

返回值的本机类型。

  • 如果值为undefined或 null,则返回“undefined”或“null”。
  • 否则,使用 Object.prototype.constructor 和 Function.prototype.name 来获取构造函数的名称。
const getType = v =>
  (v === undefined ? 'undefined' : v === null ? 'null' : v.constructor.name);
getType(new Set([1, 2, 3])); // 'Set'

获取URL 参数并转为对象

创建一个包含当前 URL 参数的对象。

  • 与适当的正则表达式一起使用String.prototype.match()以获取所有键值对。
  • 用于Array.prototype.reduce()将它们映射并组合成一个对象。
  • 作为参数传递location.search以应用于当前url.
const getURLParameters = url =>
  (url.match(/([^?=&]+)(=([^&]*))/g) || []).reduce(
    (a, v) => (
      (a[v.slice(0, v.indexOf('='))] = v.slice(v.indexOf('=') + 1)), a
    ),
    {}
  );
getURLParameters('google.com'); // {}
getURLParameters('http://url.com/page?name=Adam&surname=Smith');
// {name: 'Adam', surname: 'Smith'}

元素到顶部距离

查找给定元素到文档顶部的距离。

  • 使用 while 循环和 HTMLElement.offsetParent 向上移动给定元素的偏移父元素。
  • 为每个元素添加HTMLElement.offsetTop并返回结果。
const getVerticalOffset = el => {
  let offset = el.offsetTop,
    _el = el;
  while (_el.offsetParent) {
    _el = _el.offsetParent;
    offset += _el.offsetTop;
  }
  return offset;
};

getVerticalOffset('.my-element'); // 120

分组数组元素

根据给定的函数对数组的元素进行分组。

  • 用于Array.prototype.map()将数组的值映射到函数或属性名称。
  • 用于Array.prototype.reduce()创建一个对象,其中的键是从映射结果中生成的。
const groupBy = (arr, fn) =>
  arr
    .map(typeof fn === 'function' ? fn : val => val[fn])
    .reduce((acc, val, i) => {
      acc[val] = (acc[val] || []).concat(arr[i]);
      return acc;
    }, {});
groupBy([6.1, 4.2, 6.3], Math.floor); // {4: [4.2], 6: [6.1, 6.3]}
groupBy(['one', 'two', 'three'], 'length'); // {3: ['one', 'two'], 5: ['three']}

汉明距离

计算两个值之间的汉明距离。

  • 使用 XOR 运算符 (^) 查找两个数字之间的位差。
  • 使用 Number.prototype.toString() 转换为二进制字符串。
  • 使用 String.prototype.match() 计算并返回字符串中 1 的个数。
const hammingDistance = (num1, num2) =>
  ((num1 ^ num2).toString(2).match(/1/g) || '').length;
hammingDistance(2, 3); // 1

检查 HTML 元素是否有类名

检查给定元素是否具有指定的类。

  • 使用Element.classListandDOMTokenList.contains()检查元素是否具有指定的类。
const hasClass = (el, className) => el.classList.contains(className);
hasClass(document.querySelector('p.special'), 'special'); // true

检测是否为小数

检查数字是否有任何小数位

  • 使用模 ( %) 运算符检查数字是否可被整除1并返回结果。
const hasDecimals = num => num % 1 !== 0;

hasDecimals(1); // false
hasDecimals(1.001); // true

检查数组是否重复

检查平面数组中是否存在重复值。

  • 用于Set获取数组中的唯一值。
  • 使用Set.prototype.sizeandArray.prototype.length检查唯一值的计数是否与原始数组中的元素相同。
const hasDuplicates = arr => new Set(arr).size !== arr.length;
hasDuplicates([0, 1, 1, 2]); // true
hasDuplicates([0, 1, 2, 3]); // false

检查进程参数是否包含标识

检查当前进程的参数是否包含指定的标志。

  • 使用 Array.prototype.every() 和 Array.prototype.includes() 检查 process.argv 是否包含所有指定的标志。
  • 使用正则表达式来测试指定的标志是否以 - 或 -- 为前缀,并相应地为它们添加前缀。
const hasFlags = (...flags) =>
  flags.every(flag =>
    process.argv.includes(/^-{1,2}/.test(flag) ? flag : '--' + flag)
  );
// node myScript.js -s --test --cool=true
hasFlags('-s'); // true
hasFlags('--test', 'cool=true', '-s'); // true
hasFlags('special'); // false

检查对象是否包含指定的键

检查目标值是否存在于 JSON 对象中。

  • 检查键是否为非空并使用 Array.prototype.every() 依次检查其键到对象 obj 的内部深度。
  • 使用 Object.prototype.hasOwnProperty() 检查 obj 是否没有当前键或不是对象,停止传播并返回 false。
  • 否则将键的值分配给 obj 以在下一次迭代中使用。
  • 如果给定的键列表为空,则预先返回 false。
const hasKey = (obj, keys) => {
  return (
    keys.length > 0 &&
    keys.every(key => {
      if (typeof obj !== 'object' || !obj.hasOwnProperty(key)) return false;
      obj = obj[key];
      return true;
    })
  );
};
let obj = {
  a: 1,
  b: { c: 4 },
  'b.d': 5
};
hasKey(obj, ['a']); // true
hasKey(obj, ['b']); // true
hasKey(obj, ['b', 'c']); // true
hasKey(obj, ['b.d']); // true
hasKey(obj, ['d']); // false
hasKey(obj, ['c']); // false
hasKey(obj, ['b', 'f']); // false

检查数组是否有很多匹配项

检查一个数组是否有多个值匹配给定的函数。

  • Array.prototype.filter()结合使用以fn查找所有匹配的数组元素。
  • 用于Array.prototype.length检查是否有多个元素匹配fn
const hasMany = (arr, fn) => arr.filter(fn).length > 1;
hasMany([1, 3], x => x % 2); // true
hasMany([1, 2], x => x % 2); // false

检查数组是否只有一个匹配项

检查数组是否只有一个值与给定函数匹配。

  • Array.prototype.filter()结合使用以fn查找所有匹配的数组元素。
  • 用于Array.prototype.length检查是否只有一个元素匹配fn
const hasOne = (arr, fn) => arr.filter(fn).length === 1;
hasOne([1, 2], x => x % 2); // true
hasOne([1, 3], x => x % 2); // false

计算 SHA-256 哈希(浏览器)

使用SHA-256算法为值创建哈希。返回一个承诺。

  • 使用SubtleCrypto API 为给定值创建哈希。
  • 创建一个新的TextEncoder并用它来编码val。将其值传递给以SubtleCrypto.digest()生成给定数据的摘要。
  • 用于DataView.prototype.getUint32()从解析的ArrayBuffer.
  • 使用 将数据转换为其十六进制表示形式Number.prototype.toString()。使用 将数据添加到数组Array.prototype.push()
  • 最后,使用Array.prototype.join()将数组中的值组合hexes成一个字符串。
const hashBrowser = val =>
  crypto.subtle
    .digest('SHA-256', new TextEncoder('utf-8').encode(val))
    .then(h => {
      let hexes = [],
        view = new DataView(h);
      for (let i = 0; i < view.byteLength; i += 4)
        hexes.push(('00000000' + view.getUint32(i).toString(16)).slice(-8));
      return hexes.join('');
    });

hashBrowser(
  JSON.stringify({ a: 'a', b: [1, 2, 3, 4], foo: { c: 'bar' } })
).then(console.log);
// '04aa106279f5977f59f9067fa9712afc4aedc6f5862a8defc34552d8c7206393'

计算 SHA-256 哈希 (Node.js)

使用SHA-256算法为值创建哈希。返回一个承诺。

  • 用于使用适当的算法crypto.createHash()创建对象。Hash
  • 用于hash.update()将数据从添加valHashhash.digest()以计算数据的摘要。
  • 用于setTimeout()防止长时间操作阻塞。返回 aPromise给它一个熟悉的界面。
const crypto = require('crypto');

const hashNode = val =>
  new Promise(resolve =>
    setTimeout(
      () => resolve(crypto.createHash('sha256').update(val).digest('hex')),
      0
    )
  );

hashNode(JSON.stringify({ a: 'a', b: [1, 2, 3, 4], foo: { c: 'bar' } })).then(
  console.log
);
// '04aa106279f5977f59f9067fa9712afc4aedc6f5862a8defc34552d8c7206393'

检查数组是否具有相同的内容

无论顺序如何,检查两个数组是否包含相同的元素。

  • 在从两个数组的值创建的for...ofa 上使用循环。Set
  • 用于Array.prototype.filter()比较两个数组中每个不同值的出现次数。
  • false如果计数不匹配任何元素,则返回,true否则返回。
const haveSameContents = (a, b) => {
  for (const v of new Set([...a, ...b]))
    if (a.filter(e => e === v).length !== b.filter(e => e === v).length)
      return false;
  return true;
};
haveSameContents([1, 2, 4], [2, 4, 1]); // true

返回数组第一个元素

返回数组的头部。

  • 检查是否arr真实并具有length属性。
  • 如果可能,使用arr[0]返回第一个元素,否则返回undefined
const head = arr => (arr && arr.length ? arr[0] : undefined);
head([1, 2, 3]); // 1
head([]); // undefined
head(null); // undefined
head(undefined); // undefined

堆排序

使用堆排序算法对数字数组进行排序。

  • 使用递归。
  • 使用扩展运算符 ( ...) 克隆原始数组arr.
  • 使用闭包来声明一个变量l和一个函数heapify
  • 使用for循环并Math.floor()结合heapify使用从数组创建最大堆。
  • 使用for循环重复缩小考虑的范围,heapify根据需要使用和交换值,以便对克隆的数组进行排序。
const heapsort = arr => {
  const a = [...arr];
  let l = a.length;

  const heapify = (a, i) => {
    const left = 2 * i + 1;
    const right = 2 * i + 2;
    let max = i;
    if (left < l && a[left] > a[max]) max = left;
    if (right < l && a[right] > a[max]) max = right;
    if (max !== i) {
      [a[max], a[i]] = [a[i], a[max]];
      heapify(a, max);
    }
  };

  for (let i = Math.floor(l / 2); i >= 0; i -= 1) heapify(a, i);
  for (i = a.length - 1; i > 0; i--) {
    [a[0], a[i]] = [a[i], a[0]];
    l--;
    heapify(a, 0);
  }
  return a;
};
heapsort([6, 3, 4, 1]); // [1, 3, 4, 6]

十六进制转 RGB

如果提供了 alpha 值,则将颜色代码转换为rgb()或字符串。rgba()

  • 使用按位右移运算符并使用 &(与)运算符屏蔽位,将十六进制颜色代码(带或不带 # 前缀)转换为具有 RGB 值的字符串。
  • 如果是3位色码,先转成6位色码。
  • 如果 alpha 值与 6 位十六进制一起提供,则rgba()返回字符串。
const hexToRGB = hex => {
  let alpha = false,
    h = hex.slice(hex.startsWith('#') ? 1 : 0);
  if (h.length === 3) h = [...h].map(x => x + x).join('');
  else if (h.length === 8) alpha = true;
  h = parseInt(h, 16);
  return (
    'rgb' +
    (alpha ? 'a' : '') +
    '(' +
    (h >>> (alpha ? 24 : 16)) +
    ', ' +
    ((h & (alpha ? 0x00ff0000 : 0x00ff00)) >>> (alpha ? 16 : 8)) +
    ', ' +
    ((h & (alpha ? 0x0000ff00 : 0x0000ff)) >>> (alpha ? 8 : 0)) +
    (alpha ? `, ${h & 0x000000ff}` : '') +
    ')'
  );
};
hexToRGB('#27ae60ff'); // 'rgba(39, 174, 96, 255)'
hexToRGB('27ae60'); // 'rgb(39, 174, 96)'
hexToRGB('#fff'); // 'rgb(255, 255, 255)'

隐藏dom元素

隐藏所有指定的元素。

  • 使用展开运算符 ( ...) 并Array.prototype.forEach()应用于display: none指定的每个元素。
const hide = (...el) => [...el].forEach(e => (e.style.display = 'none'));
hide(...document.querySelectorAll('img')); // 隐藏页面上的所有 <img> 元素

HTTP Delete请求

DELETE向传递的 URL发出请求。

  • 使用XMLHttpRequestWeb API 向DELETE给定的url.
  • onload通过运行提供的函数来处理事件callback
  • onerror通过运行提供的函数来处理事件err
  • 省略第三个参数,err默认将请求记录到控制台的错误流。
const httpDelete = (url, callback, err = console.error) => {
  const request = new XMLHttpRequest();
  request.open('DELETE', url, true);
  request.onload = () => callback(request);
  request.onerror = () => err(request);
  request.send();
};

httpDelete('https://jsonplaceholder.typicode.com/posts/1', request => {
  console.log(request.responseText);
}); // Logs: {}

HTTP Get请求

GET向传递的 URL发出请求。

  • 使用XMLHttpRequestWeb API 向GET给定的url.
  • onload通过调用给定的callback来处理事件responseText
  • onerror通过运行提供的函数来处理事件err
  • 省略第三个参数,默认情况下err将错误记录到控制台的error流中。
const httpGet = (url, callback, err = console.error) => {
  const request = new XMLHttpRequest();
  request.open('GET', url, true);
  request.onload = () => callback(request.responseText);
  request.onerror = () => err(request);
  request.send();
};
httpGet(
  'https://jsonplaceholder.typicode.com/posts/1',
  console.log
); /*
Logs: {
  "userId": 1,
  "id": 1,
  "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
  "body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
}
*/

HTTP Post请求

POST向传递的 URL发出请求。

  • 使用XMLHttpRequestWeb API 向POST给定的url.
  • HTTP使用方法设置请求标头的值setRequestHeader
  • onload通过调用给定的callback来处理事件responseText
  • onerror通过运行提供的函数来处理事件err
  • 省略第四个参数,默认情况下err将错误记录到控制台的error流中。
const httpPost = (url, data, callback, err = console.error) => {
  const request = new XMLHttpRequest();
  request.open('POST', url, true);
  request.setRequestHeader('Content-type', 'application/json; charset=utf-8');
  request.onload = () => callback(request.responseText);
  request.onerror = () => err(request);
  request.send(data);
};
const newPost = {
  userId: 1,
  id: 1337,
  title: 'Foo',
  body: 'bar bar bar'
};
const data = JSON.stringify(newPost);
httpPost(
  'https://jsonplaceholder.typicode.com/posts',
  data,
  console.log
); /*
Logs: {
  "userId": 1,
  "id": 1337,
  "title": "Foo",
  "body": "bar bar bar"
}
*/
httpPost(
  'https://jsonplaceholder.typicode.com/posts',
  null, // does not send a body
  console.log
); /*
Logs: {
  "id": 101
}
*/

HTTP Put请求

PUT向传递的 URL发出请求。

  • 使用XMLHttpRequestweb api 向PUT给定的url.
  • HTTP使用方法设置请求标头的值setRequestHeader
  • onload通过运行提供的函数来处理事件callback
  • onerror通过运行提供的函数来处理事件err
  • 省略最后一个参数,err默认情况下将请求记录到控制台的错误流中。
const httpPut = (url, data, callback, err = console.error) => {
  const request = new XMLHttpRequest();
  request.open('PUT', url, true);
  request.setRequestHeader('Content-type', 'application/json; charset=utf-8');
  request.onload = () => callback(request);
  request.onerror = () => err(request);
  request.send(data);
};
const password = 'fooBaz';
const data = JSON.stringify({
  id: 1,
  title: 'foo',
  body: 'bar',
  userId: 1
});
httpPut('https://jsonplaceholder.typicode.com/posts/1', data, request => {
  console.log(request.responseText);
}); /*
Logs: {
  id: 1,
  title: 'foo',
  body: 'bar',
  userId: 1
}
*/

重定向到 HTTPS

如果当前在 HTTP 中,则将页面重定向到 HTTPS。

  • 用于location.protocol获取当前使用的协议。
  • 如果不是 HTTPS,请使用location.replace()将现有页面替换为该页面的 HTTPS 版本。
  • 用于location.href获取完整地址,将其拆分String.prototype.split()并删除 URL 的协议部分。
  • 请注意,按下后退按钮不会将其带回 HTTP 页面,因为它在历史记录中已被替换。
const httpsRedirect = () => {
  if (location.protocol !== 'https:')
    location.replace('https://' + location.href.split('//')[1]);
};
httpsRedirect();
// 如果您在 http://mydomain.com 上,您将被重定向到 https://mydomain.com

函数的执行的频率

测量函数每秒执行的次数 (hz/hertz)。

  • 用于performance.now()获取迭代循环前后以毫秒为单位的差异,以计算执行函数时间所经过的时间iterations
  • 通过将毫秒转换为秒并将其除以经过的时间来返回每秒的周期数。
  • 省略第二个参数 ,iterations以使用默认的 100 次迭代。
const hz = (fn, iterations = 100) => {
  const before = performance.now();
  for (let i = 0; i < iterations; i++) fn();
  return (1000 * iterations) / (performance.now() - before);
};
const numbers = Array(10000).fill().map((_, i) => i);

const sumReduce = () => numbers.reduce((acc, n) => acc + n, 0);
const sumForLoop = () => {
  let sum = 0;
  for (let i = 0; i < numbers.length; i++) sum += numbers[i];
  return sum;
};

Math.round(hz(sumReduce)); // 572
Math.round(hz(sumForLoop)); // 4784

范围内的数字

检查给定数字是否在给定范围内。

  • 使用算术比较来检查给定数字是否在指定范围内。
  • 如果未指定第二个参数end,则范围被认为是从0start
const inRange = (n, start, end = null) => {
  if (end && start > end) [end, start] = [start, end];
  return end == null ? n >= 0 && n < start : n >= start && n < end;
};
inRange(3, 2, 5); // true
inRange(3, 4); // true
inRange(2, 3, 5); // false
inRange(3, 2); // false

检查数组是否包含所有值

检查 中的所有元素是否values都包含在 中arr

  • 使用Array.prototype.every()和`Array.prototype.includes()检查是否所有元素都values包含在arr`.
const includesAll = (arr, values) => values.every(v => arr.includes(v));
includesAll([1, 2, 3, 4], [1, 4]); // true
includesAll([1, 2, 3, 4], [1, 5]); // false

检查数组是否包含任何值

values检查 中是否包含 的至少一个元素arr

  • 使用Array.prototype.some()和`Array.prototype.includes()检查是否至少有一个元素values包含在arr`.
const includesAny = (arr, values) => values.some(v => arr.includes(v));
includesAny([1, 2, 3, 4], [2, 9]); // true
includesAny([1, 2, 3, 4], [8, 9]); // false

不区分大小写的子字符串搜索

检查字符串是否包含子字符串,不区分大小写。

  • 使用RegExp带有标志的构造函数'i'来创建匹配给定的正则表达式searchString,忽略大小写。
  • 用于RegExp.prototype.test()检查字符串是否包含子字符串。
const includesCaseInsensitive = (str, searchString) =>
  new RegExp(searchString, 'i').test(str);
includesCaseInsensitive('Blue Whale', 'blue'); // true

缩进字符串

缩进提供的字符串中的每一行。

  • 使用和正则表达式在每行的开头String.prototype.replace()添加时间指定的字符。indent count
  • 省略第三个参数 ,indent以使用默认缩进字符' '
const indentString = (str, count, indent = ' ') =>
  str.replace(/^/gm, indent.repeat(count));
indentString('Lorem\nIpsum', 2); // '  Lorem\n  Ipsum'
indentString('Lorem\nIpsum', 2, '_'); // '__Lorem\n__Ipsum'

基于函数的对象数组转对象

从数组创建对象,使用函数将每个值映射到键。

  • 用于Array.prototype.reduce()从创建对象arr
  • 应用于fn每个值arr以生成一个键并将键值对添加到对象。
const indexBy = (arr, fn) =>
  arr.reduce((obj, v, i) => {
    obj[fn(v, i, arr)] = v;
    return obj;
  }, {});

indexBy([
  { id: 10, name: 'apple' },
  { id: 20, name: 'orange' }
], x => x.id);
// { '10': { id: 10, name: 'apple' }, '20': { id: 20, name: 'orange' } }

检索所有索引

查找数组中 val 的所有索引。 如果 val 从未出现,则返回一个空数组。

  • 用于Array.prototype.reduce()遍历元素并存储匹配元素的索引。
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); // []

查找给定字符串中子字符串的所有索引

查找给定字符串中子字符串的所有索引。

  • 用于Array.prototype.indexOf()在. searchValue_str
  • 如果找到值并更新索引,则用于yield返回索引i
  • 使用一个while循环,一旦从 返回的值Array.prototype.indexOf()是,就会终止生成器-1
const indexOfSubstrings = function* (str, searchValue) {
  let i = 0;
  while (true) {
    const r = str.indexOf(searchValue, i);
    if (r !== -1) {
      yield r;
      i = r + 1;
    } else return;
  }
};
[...indexOfSubstrings('tiktok tok tok tik tok tik', 'tik')]; // [0, 15, 23]
[...indexOfSubstrings('tutut tut tut', 'tut')]; // [0, 2, 6, 10]
[...indexOfSubstrings('hello', 'hi')]; // []

基于键的对象数组转对象

从数组创建一个对象,使用指定的键并将它从每个值中排除。

  • 用于Array.prototype.reduce()从创建对象arr
  • key使用对象解构获取给定和关联的值data,并将键值对添加到对象中。
const indexOn = (arr, key) =>
  arr.reduce((obj, v) => {
    const { [key]: id, ...data } = v;
    obj[id] = data;
    return obj;
  }, {});
indexOn([
  { id: 10, name: 'apple' },
  { id: 20, name: 'orange' }
], 'id');
// { '10': { name: 'apple' }, '20': { name: 'orange' } }

返回没有最后一个元素的数组

返回数组中除最后一个元素之外的所有元素。

  • 用于Array.prototype.slice()返回数组中除最后一个元素之外的所有元素。
const initial = arr => arr.slice(0, -1);
initial([1, 2, 3]); // [1, 2]

初始化二维数组

初始化给定宽度、高度和值的二维数组。

  • 使用Array.from()andArray.prototype.map()生成h行,其中每行都是一个新的 size 数组w
  • 用于Array.prototype.fill()初始化具有 value 的所有项目val
  • 省略最后一个参数 ,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]]

用范围初始化数组

初始化一个数组,其中包含指定范围内的数字,其中startend包含它们的公差step

  • 用于Array.from()创建所需长度的数组。
  • 使用(end - start + 1) / step和映射函数用给定范围内的所需值填充数组。
  • 省略第二个参数 ,start以使用默认值0
  • 省略最后一个参数 ,step以使用默认值1
const initializeArrayWithRange = (end, start = 0, step = 1) =>
  Array.from(
    { length: Math.ceil((end - start + 1) / step) },
    (_, i) => i * step + start
  );

initializeArrayWithRange(5); // [0, 1, 2, 3, 4, 5]
initializeArrayWithRange(7, 3); // [3, 4, 5, 6, 7]
initializeArrayWithRange(9, 0, 2); // [0, 2, 4, 6, 8]

使用反转范围初始化数组

初始化一个数组,其中包含指定范围内的数字(反向),其中startend包含它们的公差step

  • 用于Array.from()创建所需长度的数组,(end - start + 1) / step.
  • 用于用Array.prototype.map()给定范围内的所需值填充数组。
  • 省略第二个参数 ,start以使用默认值0
  • 省略最后一个参数 ,step以使用默认值1
const initializeArrayWithRangeRight = (end, start = 0, step = 1) =>
  Array.from({ length: Math.ceil((end + 1 - start) / step) }).map(
    (v, i, arr) => (arr.length - i - 1) * step + start
  );
initializeArrayWithRangeRight(5); // [5, 4, 3, 2, 1, 0]
initializeArrayWithRangeRight(7, 3); // [7, 6, 5, 4, 3]
initializeArrayWithRangeRight(9, 0, 2); // [8, 6, 4, 2, 0]

用值初始化数组

用指定的值初始化并填充一个数组。

  • 用于Array.from()创建所需长度的数组,Array.prototype.fill()以用所需的值填充它。
  • 省略最后一个参数 ,val以使用默认值0
const initializeArrayWithValues = (n, val = 0) =>
  Array.from({ length: n }).fill(val);
initializeArrayWithValues(5, 2); // [2, 2, 2, 2, 2]

初始化n维数组

创建具有给定值的 n 维数组。

  • 使用递归。
  • 使用Array.from(),Array.prototype.map()生成行,其中每行都是使用 初始化的新数组initializeNDArray()
const initializeNDArray = (val, ...args) =>
  args.length === 0
    ? val
    : Array.from({ length: args[0] }).map(() =>
        initializeNDArray(val, ...args.slice(1))
      );
initializeNDArray(1, 3); // [1, 1, 1]
initializeNDArray(5, 2, 2, 2); // [[[5, 5], [5, 5]], [[5, 5], [5, 5]]]

注入 CSS

将给定的 CSS 代码注入当前文档

  • 用于Document.createElement()创建新style元素并将其类型设置为text/css
  • 用于Element.innerText将值设置为给定的 CSS 字符串。
  • 使用Document.headandElement.appendChild()将新元素附加到文档头部。
  • 返回新创建的style元素。
const injectCSS = css => {
  let el = document.createElement('style');
  el.type = 'text/css';
  el.innerText = css;
  document.head.appendChild(el);
  return el;
};

injectCSS('body { background-color: #000 }');
// '<style type="text/css">body { background-color: #000 }</style>'

在元素后插入 HTML 字符串

在指定元素的末尾后插入一个 HTML 字符串。

  • Element.insertAdjacentHTML()与 position of一起使用,'afterend'解析htmlString并插入到 的结尾之后el
const insertAfter = (el, htmlString) =>
  el.insertAdjacentHTML('afterend', htmlString);
insertAfter(document.getElementById('myId'), '<p>after</p>');
// <div id="myId">...</div> <p>after</p>

在数组索引处插入值

改变原始数组以在指定索引后插入给定值。

  • 使用具有适当索引和删除计数 0 的 Array.prototype.splice(),分散要插入的给定值。
const insertAt = (arr, i, ...v) => {
  arr.splice(i + 1, 0, ...v);
  return arr;
};
let myArray = [1, 2, 3, 4];
insertAt(myArray, 2, 5); // myArray = [1, 2, 3, 5, 4]

let otherArray = [2, 10];
insertAt(otherArray, 0, 4, 6, 8); // otherArray = [2, 4, 6, 8, 10]

在元素前插入 HTML 字符串

在指定元素的开头之前插入一个 HTML 字符串。

  • Element.insertAdjacentHTML()与 position of一起使用,'beforebegin'解析htmlString并插入到 的开头之前el
const insertBefore = (el, htmlString) =>
  el.insertAdjacentHTML('beforebegin', htmlString);
insertBefore(document.getElementById('myId'), '<p>before</p>');
// <p>before</p> <div id="myId">...</div>

插入排序

使用插入排序算法对数字数组进行排序。

  • 用于Array.prototype.reduce()迭代给定数组中的所有元素。
  • 如果length累加器的 是0,则将当前元素添加到它。
  • 用于Array.prototype.some()迭代累加器中的结果,直到找到正确的位置。
  • 用于Array.prototype.splice()将当前元素插入累加器。
const insertionSort = arr =>
  arr.reduce((acc, x) => {
    if (!acc.length) return [x];
    acc.some((y, j) => {
      if (x <= y) {
        acc.splice(j, 0, x);
        return true;
      }
      if (x > y && j === acc.length - 1) {
        acc.splice(j + 1, 0, x);
        return true;
      }
      return false;
    });
    return acc;
  }, []);
insertionSort([6, 3, 4, 1]); // [1, 3, 4, 6]

数组交集

返回两个数组中都存在的元素,过滤重复值。

  • 创建一个Setfrom b,然后使用Array.prototype.filter()ona只保留包含在 中的值b
const intersection = (a, b) => {
  const s = new Set(b);
  return [...new Set(a)].filter(x => s.has(x));
};

intersection([1, 2, 3], [4, 3, 2]); // [2, 3]

基于函数的数组交集

使用提供的比较器函数返回两个数组中都存在的元素。

  • 使用Array.prototype.filter()Array.prototype.findIndex()结合提供的比较器来确定相交值。
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]

检查两个数组是否相交

确定两个数组是否具有共同项。

  • 从 b 创建一个 Set 以获取 b 中的唯一值。
  • 使用 Array.prototype.some() 在 a 上使用 Set.prototype.has() 检查它的任何值是否包含在 b 中。
const intersects = (a, b) => {
  const s = new Set(b);
  return [...new Set(a)].some(x => s.has(x));
};
intersects(['a', 'b'], ['b', 'c']); // true
intersects(['a', 'b'], ['c', 'd']); // false

反转对象键值对

反转对象的键值对,而不改变它。

  • 使用Object.keys()和`Array.prototype.reduce()`反转对象的键值对并应用提供的函数(如果有)。
  • 省略第二个参数 ,fn以在不对它们应用函数的情况下获取倒置键。
  • 每个倒排键对应的倒排值是一个负责生成倒排值的键数组。如果提供了一个函数,它会应用于每个倒置的键。
const invertKeyValues = (obj, fn) =>
  Object.keys(obj).reduce((acc, key) => {
    const val = fn ? fn(obj[key]) : obj[key];
    acc[val] = acc[val] || [];
    acc[val].push(key);
    return acc;
  }, {});
invertKeyValues({ a: 1, b: 2, c: 1 }); // { 1: [ 'a', 'c' ], 2: [ 'b' ] }
invertKeyValues({ a: 1, b: 2, c: 1 }, value => 'group' + value);
// { group1: [ 'a', 'c' ], group2: [ 'b' ] }

检查值是否属于类型

检查提供的值是否属于指定类型。

  • 确保该值不是undefinednull正在使用Array.prototype.includes()
  • 用于Object.prototype.constructor比较值的构造函数属性type以检查提供的值是否为指定的type
const is = (type, val) => ![, null].includes(val) && val.constructor === type;
is(Array, [1]); // true
is(ArrayBuffer, new ArrayBuffer()); // true
is(Map, new Map()); // true
is(RegExp, /./g); // true
is(Set, new Set()); // true
is(WeakMap, new WeakMap()); // true
is(WeakSet, new WeakSet()); // true
is(String, ''); // true
is(String, new String('')); // true
is(Number, 1); // true
is(Number, new Number(1)); // true
is(Boolean, true); // true
is(Boolean, new Boolean(true)); // true

检查是否是URL

检查给定的字符串是否是绝对 URL。

  • 用于RegExp.prototype.test()测试字符串是否为绝对 URL。
const isAbsoluteURL = str => /^[a-z][a-z0-9+.-]*:/.test(str);
isAbsoluteURL('https://google.com'); // true
isAbsoluteURL('ftp://www.myserver.net'); // true
isAbsoluteURL('/foo/bar'); // false

检查日期是否在另一个日期之后

检查一个日期是否在另一个日期之后。

  • 使用大于运算符 ( >) 检查第一个日期是否在第二个日期之后。
const isAfterDate = (dateA, dateB) => dateA > dateB;
isAfterDate(new Date(2010, 10, 21), new Date(2010, 10, 20)); // true

字符串是否全是字母

检查字符串是否只包含字母字符。

  • 用于RegExp.prototype.test()检查给定的字符串是否与字母正则表达式模式匹配。
const isAlpha = str => /^[a-zA-Z]*$/.test(str);
isAlpha('sampleInput'); // true
isAlpha('this Will fail'); // false
isAlpha('123'); // false

字符串是字母和数字

检查字符串是否仅包含字母数字字符。

  • 用于RegExp.prototype.test()检查输入字符串是否与字母数字正则表达式模式匹配。
const isAlphaNumeric = str => /^[a-z0-9]+$/gi.test(str);
isAlphaNumeric('hello123'); // true
isAlphaNumeric('123'); // true
isAlphaNumeric('hello 123'); // false (space character is not alphanumeric)
isAlphaNumeric('#$hello'); // false

判断字符串是否是相同字符

检查一个字符串是否是另一个字符串的变位词(不区分大小写,忽略空格、标点符号和特殊字符)。

  • String.prototype.toLowerCase()将andString.prototype.replace()与适当的正则表达式一起使用以删除不需要的字符。
  • 在两个字符串上使用String.prototype.split(),Array.prototype.sort()Array.prototype.join()对它们进行规范化,然后检查它们的规范化形式是否相等。
const isAnagram = (str1, str2) => {
  const normalize = str =>
    str
      .toLowerCase()
      .replace(/[^a-z0-9]/gi, '')
      .split('')
      .sort()
      .join('');
  return normalize(str1) === normalize(str2);
};
isAnagram('iceman', 'cinema'); // true

判断是否为类数组

检查提供的参数是否类似于数组(即可迭代)。

  • 检查提供的参数是否不是null以及它的Symbol.iterator属性是否是一个函数。
const isArrayLike = obj =>
  obj != null && typeof obj[Symbol.iterator] === 'function';
isArrayLike([1, 2, 3]); // true
isArrayLike(document.querySelectorAll('.className')); // true
isArrayLike('abc'); // true
isArrayLike(null); // false

判断值是否为异步函数

检查给定的参数是否是一个async函数。

  • 使用Object.prototype.toString()andFunction.prototype.call()检查结果是否为'[object AsyncFunction]'
const isAsyncFunction = val =>
  Object.prototype.toString.call(val) === '[object AsyncFunction]';
isAsyncFunction(function() {}); // false
isAsyncFunction(async function() {}); // true

检查日期是否早于另一个日期

检查一个日期是否早于另一个日期。

  • 使用小于运算符 ( <) 检查第一个日期是否在第二个日期之前。
const isBeforeDate = (dateA, dateB) => dateA < dateB;
isBeforeDate(new Date(2010, 10, 20), new Date(2010, 10, 21)); // true

检查日期是否在两个日期之间

检查一个日期是否在另外两个日期之间。

  • 使用大于 ( >) 和小于 ( <) 运算符来检查是否date介于dateStart和 之间dateEnd
const isBetweenDates = (dateStart, dateEnd, date) =>
  date > dateStart && date < dateEnd;
isBetweenDates(
  new Date(2010, 11, 20),
  new Date(2010, 11, 30),
  new Date(2010, 11, 19)
); // false
isBetweenDates(
  new Date(2010, 11, 20),
  new Date(2010, 11, 30),
  new Date(2010, 11, 25)
); // true

检测是否为布尔值

检查给定参数是否为布尔值。

  • 用于typeof检查一个值是否被归类为布尔值。
const isBoolean = val => typeof val === 'boolean';
isBoolean(null); // false
isBoolean(false); // true

判断环境是否为浏览器

判断当前运行环境是否为浏览器,以便前端模块可以在服务器(Node)上运行而不会抛出错误。

  • 在和的值Array.prototype.includes()上使用(全局变量通常只在浏览器环境中可用,除非它们被明确定义),如果其中之一是.typeof`WindowDocumenttrue`undefined
  • typeof允许在不抛出ReferenceError.
  • 如果两者都不是undefined,则认为当前环境是浏览器。
const isBrowser = () => ![typeof window, typeof document].includes('undefined');
isBrowser(); // true (browser)
isBrowser(); // false (Node)

检查浏览器选项卡是否获得焦点

检查页面的浏览器选项卡是否获得焦点。

  • 使用页面可见性 APIDocument.hidden引入的属性来检查页面的浏览器选项卡是可见还是隐藏。
const isBrowserTabFocused = () => !document.hidden;
isBrowserTabFocused(); // true

数组包含在其他数组中

无论顺序如何,检查第一个数组的元素是否包含在第二个数组中。

  • 在从第一个数组创建的for...ofa 上使用循环。Set
  • 用于Array.prototype.some()检查第二个数组中是否包含所有不同的值。
  • 用于Array.prototype.filter()比较两个数组中每个不同值的出现次数。
  • false如果第一个数组中任何元素的计数大于第二个数组,则返回,否则返回true
const isContainedIn = (a, b) => {
  for (const v of new Set(a)) {
    if (
      !b.some(e => e === v) ||
      a.filter(e => e === v).length > b.filter(e => e === v).length
    )
      return false;
  }
  return true;
};

isContainedIn([1, 4], [2, 4, 1]); // true

检查日期是否有效

检查是否可以从给定值创建有效的日期对象。

  • 使用扩展运算符 ( ...) 将参数数组传递给Date构造函数。
  • 使用Date.prototype.valueOf()和`Number.isNaN()检查是否Date`可以从给定值创建有效对象。
const isDateValid = (...val) => !Number.isNaN(new Date(...val).valueOf());
isDateValid('December 17, 1995 03:24:00'); // true
isDateValid('1995-12-17T03:24:00'); // true
isDateValid('1995-12-17 T03:24:00'); // false
isDateValid('Duck'); // false
isDateValid(1995, 11, 17); // true
isDateValid(1995, 11, 17, 'Duck'); // false
isDateValid({}); // false

检查对象是否深度冻结

检查对象是否深度冻结。

  • 使用递归。
  • Object.isFrozen()在给定的对象上使用。
  • 使用Object.keys(),Array.prototype.every()检查所有键是深度冻结对象还是非对象值。
const isDeepFrozen = obj =>
  Object.isFrozen(obj) &&
  Object.keys(obj).every(
    prop => typeof obj[prop] !== 'object' || isDeepFrozen(obj[prop])
  );

const x = Object.freeze({ a: 1 });
const y = Object.freeze({ b: { c: 2 } });
isDeepFrozen(x); // true
isDeepFrozen(y); // false

脱节的迭代

检查两个可迭代对象是否脱节(没有共同的值)。

  • 使用Set构造函数从每个可迭代对象创建一个新Set对象。
  • 使用Array.prototype.every()andSet.prototype.has()检查两个可迭代对象是否没有共同的值。
const isDisjoint = (a, b) => {
  const sA = new Set(a), sB = new Set(b);
  return [...sA].every(v => !sB.has(v));
};
isDisjoint(new Set([1, 2]), new Set([3, 4])); // true

数字是整除的

检查第一个数字参数是否可以被第二个参数整除。

  • 使用模运算符 ( %) 检查余数是否等于0
const isDivisible = (dividend, divisor) => dividend % divisor === 0;

isDivisible(6, 3); // true

参数是否是双工

检查给定的参数是否是双工(可读和可写)流。

  • 检查该值是否不同于 null。
  • 使用 typeof 检查值是否为对象类型,property属性是否为函数类型。
  • 另外检查 _read、_write 和 _readableState、_writableState 属性的类型是否分别为函数和对象。
const isDuplexStream = val =>
  val !== null &&
  typeof val === 'object' &&
  typeof val.pipe === 'function' &&
  typeof val._read === 'function' &&
  typeof val._readableState === 'object' &&
  typeof val._write === 'function' &&
  typeof val._writableState === 'object';
const Stream = require('stream');
isDuplexStream(new Stream.Duplex()); // true

检测集合是否为空

检查 a 值是否为空对象/集合、是否没有可枚举属性或是否为任何不被视为集合的类型。

  • 检查提供的值是否为 null 或者其长度是否等于 0。
const isEmpty = val => val == null || !(Object.keys(val) || val).length;
isEmpty([]); // true
isEmpty({}); // true
isEmpty(''); // true
isEmpty([1, 2]); // false
isEmpty({ a: 1, b: 2 }); // false
isEmpty('text'); // false
isEmpty(123); // true - 类型不被视为集合
isEmpty(true); // true - 类型不被视为集合

数字是否是偶数

检查给定的数字是否为偶数。

  • 使用模 ( ) 运算符检查数字是奇数还是偶数%
  • true如果数字是偶数则返回,false如果数字是奇数则返回。
const isEven = num => num % 2 === 0;
isEven(3); // false

是否是一个函数

检查给定的参数是否是一个函数。

  • 用于typeof检查值是否被归类为函数原语。
const isFunction = val => typeof val === 'function';

isFunction('x'); // false
isFunction(x => x); // true

值是生成函数

检查给定的参数是否是生成器函数。

  • 使用Object.prototype.toString()和`Function.prototype.call()检查结果是否为'[object GeneratorFunction]'`。
const isGeneratorFunction = val =>
  Object.prototype.toString.call(val) === '[object GeneratorFunction]';
isGeneratorFunction(function() {}); // false
isGeneratorFunction(function*() {}); // true

字符串是 ISO 格式的日期

检查给定的字符串在简化的扩展 ISO 格式 (ISO 8601) 中是否有效。

  • 使用构造函数从给定的字符串Date创建一个对象。Date
  • 使用Date.prototype.valueOf()andNumber.isNaN()检查生成的日期对象是否有效。
  • 用于Date.prototype.toISOString()将日期的 ISO 格式字符串表示与原始字符串进行比较。
const isISOString = val => {
  const d = new Date(val);
  return !Number.isNaN(d.valueOf()) && d.toISOString() === val;
};

isISOString('2020-10-12T10:10:10.000Z'); // true
isISOString('2020-10-12'); // false

检查闰年

检查给定的year年份是否为闰年。

  • 使用Date构造函数,将日期设置为给定的 2 月 29 日year
  • 用于Date.prototype.getMonth()检查月份是否等于1
const isLeapYear = year => new Date(year, 1, 29).getMonth() === 1;
isLeapYear(2019); // false
isLeapYear(2020); // true

检查 localStorage 是否启用

检查是否localStorage启用。

  • 如果所有操作都成功完成,则使用try...catch块返回,否则。true`false`
  • 使用Storage.setItem()andStorage.removeItem()测试在Window.localStorage.
const isLocalStorageEnabled = () => {
  try {
    const key = `__storage__test`;
    window.localStorage.setItem(key, null);
    window.localStorage.removeItem(key);
    return true;
  } catch (e) {
    return false;
  }
};

isLocalStorageEnabled(); // true, 如果 localStorage 可以访问

字符串是小写的

检查字符串是否为小写。

  • 将给定的字符串转换为小写,使用String.prototype.toLowerCase()并将其与原始字符串进行比较。
const isLowerCase = str => str === str.toLowerCase();
isLowerCase('abc'); // true
isLowerCase('a3@$'); // true
isLowerCase('Ab4'); // false

数字为负零

检查给定值是否等于负零 ( -0)。

  • 检查传递的值是否等于0以及1除以该值是否等于-Infinity
const isNegativeZero = val => val === 0 && 1 / val === -Infinity;

isNegativeZero(-0); // true
isNegativeZero(0); // false

值为零

检查指定的值是否为nullundefined

  • 使用严格相等运算符检查 的值是否val等于nullundefined
const isNil = val => val === undefined || val === null;
isNil(null); // true
isNil(undefined); // true
isNil(''); // false

环境是 Node.js

判断当前运行环境是否为 Node.js。

  • 使用process提供有关当前 Node.js 进程信息的全局对象。
  • 检查process,process.versions和 是否process.versions.node被定义。
const isNode = () =>
  typeof process !== 'undefined' &&
  !!process.versions &&
  !!process.versions.node;

isNode(); // true (Node)
isNode(); // false (browser)

值为空(null)

检查指定的值是否为null

  • 使用严格相等运算符检查 的值是否val等于null
const isNull = val => val === null;
isNull(null); // true

值是数字

检查给定的参数是否为数字。

  • 用于typeof检查一个值是否被归类为数字基元。
  • 为了防止 NaN,检查是否 val === val(因为 NaN 的 typeof 等于 number 并且是唯一不等于自身的值)。
const isNumber = val => typeof val === 'number' && val === val;

isNumber(1); // true
isNumber('1'); // false
isNumber(NaN); // false

值是对象

检查传递的值是否为对象。

  • 使用Object构造函数为给定值创建对象包装器。
  • 如果值为nullor undefined,则创建并返回一个空对象。
  • 否则,返回对应于给定值的类型的对象。
const isObject = obj => obj === Object(obj);
isObject([1, 2, 3, 4]); // true
isObject([]); // true
isObject(['Hello!']); // true
isObject({ a: 1 }); // true
isObject({}); // true
isObject(true); // false

值类似于对象

检查一个值是否类似于对象。

  • 检查提供的值是否不为 null 且其 typeof 是否等于“object”。
const isObjectLike = val => val !== null && typeof val === 'object';
isObjectLike({}); // true
isObjectLike([1, 2, 3]); // true
isObjectLike(x => x); // false
isObjectLike(null); // false

数字是奇数

检查给定的数字是否为奇数。

  • 使用取模 (%) 运算符检查数字是奇数还是偶数。
  • 如果数字是奇数返回 true,如果数字是偶数则返回 false。
const isOdd = num => num % 2 === 1;

isOdd(3); // true

值是普通对象

检查提供的值是否是由 Object 构造函数创建的对象。

  • 检查提供的值是否真实。
  • 用于typeof检查它是否是一个对象并Object.prototype.constructor确保构造函数等于Object
const isPlainObject = val =>
  !!val && typeof val === 'object' && val.constructor === Object;
isPlainObject({ a: 1 }); // true
isPlainObject(new Map()); // false

数字是十的幂

检查给定数字是否是 的幂10

  • 使用Math.log10()和 模运算符 ( %) 确定是否n10的幂。
const isPowerOfTen = n => Math.log10(n) % 1 === 0;

isPowerOfTen(1); // true
isPowerOfTen(10); // true
isPowerOfTen(20); // false

数是二的幂

检查给定数字是否是 的幂2

  • 使用按位二进制 AND 运算符 ( &) 确定是否n2的幂。
  • 此外,检查它n不是假的。
const isPowerOfTwo = n => !!n && (n & (n - 1)) == 0;
isPowerOfTwo(0); // false
isPowerOfTwo(1); // true
isPowerOfTwo(8); // true

数是质数

检查提供的整数是否为素数。

  • 检查从 2 到给定数字的平方根的数字。
  • 如果其中任何一个能整除给定数,则返回 false,否则返回 true,除非该数小于 2。
const isPrime = num => {
  const boundary = Math.floor(Math.sqrt(num));
  for (let i = 2; i <= boundary; i++) if (num % i === 0) return false;
  return num >= 2;
};

isPrime(11); // true

值是原始的

检查传递的值是否为原始值。

  • 创建一个对象val并将其与val以确定传递的值是否为原始值(即不等于创建的对象)。
const isPrimitive = val => Object(val) !== val;
isPrimitive(null); // true
isPrimitive(undefined); // true
isPrimitive(50); // true
isPrimitive('Hello!'); // true
isPrimitive(false); // true
isPrimitive(Symbol()); // true
isPrimitive([]); // false
isPrimitive({}); // false

是否为promise

检查对象是否看起来像Promise.

  • 检查对象是否不为null,它的类型是否匹配object或function,以及它是否具有 .then 属性,这也是一个function。
const isPromiseLike = obj =>
  obj !== null &&
  (typeof obj === 'object' || typeof obj === 'function') &&
  typeof obj.then === 'function';

isPromiseLike({
  then: function() {
    return '';
  }
}); // true
isPromiseLike(null); // false
isPromiseLike({}); // false

检查给定的参数是否是可读流。

  • 检查该值是否与 不同null
  • 用于typeof检查值是否为 object类型以及pipe属性是否为 function类型
  • 另外检查 _read 和 _readableState 属性的类型是否分别为function和object。
const isReadableStream = val =>
  val !== null &&
  typeof val === 'object' &&
  typeof val.pipe === 'function' &&
  typeof val._read === 'function' &&
  typeof val._readableState === 'object';
const fs = require('fs');

isReadableStream(fs.createReadStream('test.txt')); // true

日期与另一个日期相同

检查一个日期是否与另一个日期相同。

  • 使用Date.prototype.toISOString()和严格相等检查 ( ===) 检查第一个日期是否与第二个日期相同。
const isSameDate = (dateA, dateB) =>
  dateA.toISOString() === dateB.toISOString();

isSameDate(new Date(2010, 10, 20), new Date(2010, 10, 20)); // true

同源网址

检查两个 URL 是否来自同一来源。

  • 使用URL.protocolandURL.host检查两个 URL 是否具有相同的协议和主机。
const isSameOrigin = (origin, destination) =>
  origin.protocol === destination.protocol && origin.host === destination.host;

const origin = new URL('https://www.30secondsofcode.org/about');
const destination = new URL('https://www.30secondsofcode.org/contact');
isSameOrigin(origin, destination); // true
const other = new URL('https://developer.mozilla.org);
isSameOrigin(origin, other); // false

检查是否启用了 sessionStorage

检查是否sessionStorage启用。

  • 如果所有操作都成功完成,则使用 try...catch 块返回 true,否则返回 false。
  • 使用 Storage.setItem() 和 Storage.removeItem() 测试在 Window.sessionStorage 中存储和删除值。
const isSessionStorageEnabled = () => {
  try {
    const key = `__storage__test`;
    window.sessionStorage.setItem(key, null);
    window.sessionStorage.removeItem(key);
    return true;
  } catch (e) {
    return false;
  }
};

isSessionStorageEnabled(); // true, if sessionStorage is accessible

数组是否已排序

检查数值数组是否已排序。

  • direction计算第一对相邻数组元素的顺序。
  • 如果给定数组为空、只有一个元素或任何一对相邻数组元素的方向发生变化,则返回 0。
  • 使用 Math.sign() 将 direction 的最终值转换为 -1(降序)或 1(升序)。
const isSorted = arr => {
  if (arr.length <= 1) return 0;
  const direction = arr[1] - arr[0];
  for (let i = 2; i < arr.length; i++) {
    if ((arr[i] - arr[i - 1]) * direction < 0) return 0;
  }
  return Math.sign(direction);
};
isSorted([0, 1, 2, 2]); // 1
isSorted([4, 3, 2]); // -1
isSorted([4, 3, 5]); // 0
isSorted([4]); // 0

值是流(stream)

检查给定的参数是否是一个流。

  • 检查该值是否与 不同null
  • 用于typeof检查值是否为 typeobject以及pipe属性是否为 type function
const isStream = val =>
  val !== null && typeof val === 'object' && typeof val.pipe === 'function';
const fs = require('fs');

isStream(fs.createReadStream('test.txt')); // true

值为字符串

检查给定的参数是否为字符串。仅适用于字符串原语。

  • 用于typeof检查值是否被归类为字符串基元。
const isString = val => typeof val === 'string';
isString('10'); // true

值是symbol

检查给定的参数是否是一个符号。

  • 用于typeof检查值是否被归类为符号基元。
const isSymbol = val => typeof val === 'symbol';

isSymbol(Symbol('x')); // true

环境是 Travis CI

检查当前环境是否为Travis CI

  • 检查当前环境是否有TRAVISCI环境变量(参考)。
const isTravisCI = () => 'TRAVIS' in process.env && 'CI' in process.env;

isTravisCI(); // true (如果代码在 Travis CI 上运行)

值未定义

检查指定的值是否为undefined

  • 使用严格相等运算符检查是否val等于undefined
const isUndefined = val => val === undefined;
isUndefined(undefined); // true

字符串是大写的

检查字符串是否为大写。

  • 将给定的字符串转换为大写,使用String.prototype.toUpperCase()并将其与原始字符串进行比较。
const isUpperCase = str => str === str.toUpperCase();
isUpperCase('ABC'); // true
isUpperCase('A3@$'); // true
isUpperCase('aB4'); // false

字符串是有效的 JSON

检查提供的字符串是否是有效的 JSON。

  • 使用JSON.parse()try...catch块来检查提供的字符串是否是有效的 JSON。
const isValidJSON = str => {
  try {
    JSON.parse(str);
    return true;
  } catch (e) {
    return false;
  }
};
isValidJSON('{"name":"Adam","age":20}'); // true
isValidJSON('{"name":"Adam",age:"20"}'); // false
isValidJSON(null); // true

日期是工作日

检查给定日期是否为工作日。

  • 使用 Date.prototype.getDay() 通过模运算符 (%) 检查工作日。
  • 省略参数 d,以使用当前日期作为默认日期。
const isWeekday = (d = new Date()) => d.getDay() % 6 !== 0;
isWeekday(); // true (如果当前日期是 2019-07-19)

日期是周末

检查给定日期是否为周末。

  • 用于Date.prototype.getDay()通过模运算符 ( %) 检查周末。
  • 省略参数 ,d以使用当前日期作为默认日期。
const isWeekend = (d = new Date()) => d.getDay() % 6 === 0;
isWeekend(); // 2018-10-19 (如果当前日期是 2018-10-18)

流是可写的

检查给定的参数是否是可写流。

  • 检查该值是否与null不同。
  • 使用 typeof 检查值是否为对象类型,管道属性是否为函数类型。
  • 另外检查 _write 和 _writableState 属性的类型是否分别是函数和对象。
const isWritableStream = val =>
  val !== null &&
  typeof val === 'object' &&
  typeof val.pipe === 'function' &&
  typeof val._write === 'function' &&
  typeof val._writableState === 'object';

const fs = require('fs');

isWritableStream(fs.createWriteStream('test.txt')); // true

将数组拼接成字符串

将数组的所有元素连接成一个字符串并返回该字符串。使用分隔符和结束分隔符。

  • 用于Array.prototype.reduce()将元素组合成一个字符串。
  • 省略第二个参数 ,separator以使用默认分隔符','.
  • 省略第三个参数 ,end以使用与默认值相同的值separator
const join = (arr, separator = ',', end = separator) =>
  arr.reduce(
    (acc, val, i) =>
      i === arr.length - 2
        ? acc + val + end
        : i === arr.length - 1
          ? acc + val
          : acc + val + separator,
    ''
  );

join(['pen', 'pineapple', 'apple', 'pen'],',','&'); // 'pen,pineapple,apple&pen'
join(['pen', 'pineapple', 'apple', 'pen'], ','); // 'pen,pineapple,apple,pen'
join(['pen', 'pineapple', 'apple', 'pen']); // 'pen,pineapple,apple,pen'

并列功能

将几个函数作为参数并返回一个函数,该函数是这些函数的并置。

  • 使用 Array.prototype.map() 返回一个可以接受可变数量参数的 fn。
  • 调用 fn 时,返回一个数组,其中包含将每个 fn 应用于 args 的结果。
const juxt = (...fns) => (...args) => [...fns].map(fn => [...args].map(fn));
juxt(
  x => x + 1,
  x => x - 1,
  x => x * 10
)(1, 2, 3); // [[2, 3, 4], [0, 1, 2], [10, 20, 30]]
juxt(
  s => s.length,
  s => s.split(' ').join('-')
)('30 seconds of code'); // [[18], ['30-seconds-of-code']]

k均值聚类算法

k使用k-means 聚类算法将给定数据分组。

  • 使用Array.from()Array.prototype.slice()为集群初始化适当的变量centroidsdistancesclasses
  • 使用 while 循环重复赋值和更新步骤,只要前一次迭代中有变化,如 itr 所示。
  • 使用 Math.hypot()、Object.keys() 和 Array.prototype.map() 计算每个数据点和质心之间的欧氏距离。
  • 使用 Array.prototype.indexOf() 和 Math.min() 找到最近的centroid。
  • 使用 Array.from() 和 Array.prototype.reduce(),以及 parseFloat() 和 Number.prototype.toFixed() 来计算新的centroids。
const kMeans = (data, k = 1) => {
  const centroids = data.slice(0, k);
  const distances = Array.from({ length: data.length }, () =>
    Array.from({ length: k }, () => 0)
  );
  const classes = Array.from({ length: data.length }, () => -1);
  let itr = true;

  while (itr) {
    itr = false;

    for (let d in data) {
      for (let c = 0; c < k; c++) {
        distances[d][c] = Math.hypot(
          ...Object.keys(data[0]).map(key => data[d][key] - centroids[c][key])
        );
      }
      const m = distances[d].indexOf(Math.min(...distances[d]));
      if (classes[d] !== m) itr = true;
      classes[d] = m;
    }

    for (let c = 0; c < k; c++) {
      centroids[c] = Array.from({ length: data[0].length }, () => 0);
      const size = data.reduce((acc, _, d) => {
        if (classes[d] === c) {
          acc++;
          for (let i in data[0]) centroids[c][i] += data[d][i];
        }
        return acc;
      }, 0);
      for (let i in data[0]) {
        centroids[c][i] = parseFloat(Number(centroids[c][i] / size).toFixed(2));
      }
    }
  }

  return classes;
};
kMeans([[0, 0], [0, 1], [1, 3], [2, 0]], 2); // [0, 1, 1, 0]

K-最近邻算法

使用k 最近邻算法对相对于标记数据集的数据点进行分类。

  • 使用 Array.prototype.map() 将数据映射到对象。 每个对象包含元素到点的欧氏距离,使用 Math.hypot()、Object.keys() 及其标签计算。
  • 使用 Array.prototype.sort() 和 Array.prototype.slice() 获取点的 k 个最近邻居。
  • 将 Array.prototype.reduce() 与 Object.keys() 和 Array.prototype.indexOf() 结合使用,以找到其中最常见的标签。
const kNearestNeighbors = (data, labels, point, k = 3) => {
  const kNearest = data
    .map((el, i) => ({
      dist: Math.hypot(...Object.keys(el).map(key => point[key] - el[key])),
      label: labels[i]
    }))
    .sort((a, b) => a.dist - b.dist)
    .slice(0, k);

  return kNearest.reduce(
    (acc, { label }, i) => {
      acc.classCounts[label] =
        Object.keys(acc.classCounts).indexOf(label) !== -1
          ? acc.classCounts[label] + 1
          : 1;
      if (acc.classCounts[label] > acc.topClassCount) {
        acc.topClassCount = acc.classCounts[label];
        acc.topClass = label;
      }
      return acc;
    },
    {
      classCounts: {},
      topClass: kNearest[0].label,
      topClassCount: 0
    }
  ).topClass;
};
const data = [[0, 0], [0, 1], [1, 3], [2, 0]];
const labels = [0, 1, 1, 0];

kNearestNeighbors(data, labels, [1, 2], 2); // 1
kNearestNeighbors(data, labels, [1, 0], 2); // 0

公里转英里

将公里转换为英里。

  • 遵循转换公式mi = km * 0.621371
const kmToMiles = km => km * 0.621371;
kmToMiles(8.1) // 5.0331051

最后一个数组元素

返回数组中的最后一个元素。

  • 检查是否arr真实并具有length属性。
  • 用于Array.prototype.length计算给定数组的最后一个元素的索引并返回它,否则返回undefined
const last = arr => (arr && arr.length ? arr[arr.length - 1] : undefined);
last([1, 2, 3]); // 3
last([]); // undefined
last(null); // undefined
last(undefined); // undefined

一个月的最后一天

返回给定日期月份中最后一个日期的字符串表示形式。

  • 使用Date.prototype.getFullYear(),Date.prototype.getMonth()从给定日期获取当前年份和月份。
  • 使用Date构造函数创建一个新日期,其中给定的年份和月份递增1,日期设置为0(上个月的最后一天)。
  • 省略参数 ,date默认使用当前日期。
const lastDateOfMonth = (date = new Date()) => {
  let d = new Date(date.getFullYear(), date.getMonth() + 1, 0);
  return d.toISOString().split('T')[0];
};
lastDateOfMonth(new Date('2015-08-11')); // '2015-08-30'

最后 n 个元素

获取n数组的最后一个元素。

  • Array.prototype.slice()与起始值一起使用-n以获取 的最后一个n元素arr
const lastN = (arr, n) => arr.slice(-n);
lastN(['a', 'b', 'c', 'd'], 2); // ['c', 'd']

最小公倍数

计算两个或多个数字的最小公倍数。

  • 使用最大公约数(GCD)公式和事实lcm(x, y) = x * y / gcd(x, y)确定最小公倍数。
  • GCD 公式使用递归。
const lcm = (...arr) => {
  const gcd = (x, y) => (!y ? x : gcd(y, x % y));
  const _lcm = (x, y) => (x * y) / gcd(x, y);
  return [...arr].reduce((a, b) => _lcm(a, b));
};

lcm(12, 7); // 84
lcm(...[1, 3, 4, 5]); // 60

左子串生成器

生成给定字符串的所有左子字符串。

  • String.prototype.length如果字符串为空,则用于提前终止。
  • 从头开始对给定字符串的每个子字符串使用for...in循环 and 。String.prototype.slice()`yield`
const leftSubstrGenerator = function* (str) {
  if (!str.length) return;
  for (let i in str) yield str.slice(0, i + 1);
};

[...leftSubstrGenerator('hello')];
// [ 'h', 'he', 'hel', 'hell', 'hello' ]

莱文斯坦距离(Levenshtein距离)

使用Levenshtein 距离算法计算两个字符串之间的差异。

  • 如果两个字符串中的任何一个的长度为零,则返回另一个字符串的长度。
  • 使用 for 循环迭代目标字符串的字母,使用嵌套的 for 循环迭代源字符串的字母。
  • 计算在目标和源中分别替换对应于 i - 1 和 j - 1 的字母的成本(如果相同则为 0,否则为 1)。
  • 使用 Math.min() 填充二维数组中的每个元素,其中上方单元格的最小值递增 1、左侧单元格递增 1 或左上角单元格递增先前计算的成本。
  • 返回生成的数组最后一行的最后一个元素。
const levenshteinDistance = (s, t) => {
  if (!s.length) return t.length;
  if (!t.length) return s.length;
  const arr = [];
  for (let i = 0; i <= t.length; i++) {
    arr[i] = [i];
    for (let j = 1; j <= s.length; j++) {
      arr[i][j] =
        i === 0
          ? j
          : Math.min(
              arr[i - 1][j] + 1,
              arr[i][j - 1] + 1,
              arr[i - 1][j - 1] + (s[j - 1] === t[i - 1] ? 0 : 1)
            );
    }
  }
  return arr[t.length][s.length];
};
levenshteinDistance('duck', 'dark'); // 2

线性搜索算法

使用线性搜索算法查找数组中给定元素的第一个索引。

  • 使用for...in循环遍历给定数组的索引。
  • 检查相应索引中的元素是否等于item
  • 如果找到该元素,则返回索引,使用一元运算+符将其从字符串转换为数字。
  • 如果在遍历整个数组后没有找到该元素,则返回-1
const linearSearch = (arr, item) => {
  for (const i in arr) {
    if (arr[i] === item) return +i;
  }
  return -1;
};
linearSearch([2, 9, 9], 9); // 1
linearSearch([2, 9, 9], 7); // -1

只监听一次事件

向元素添加事件侦听器,该元素仅在第一次触发事件时运行回调。

  • 用于EventTarget.addEventListener()向元素添加事件侦听器。
  • 用作{ once: true }选项只运行一次给定的回调。
const listenOnce = (el, evt, fn) =>
  el.addEventListener(evt, fn, { once: true });
listenOnce(
  document.getElementById('my-id'),
  'click',
  () => console.log('Hello world')
); // 'Hello world' 只会在第一次点击时被记录

将对象映射到数组

使用提供的映射函数将对象映射到对象数组。

  • 用于Object.entries()获取对象的键值对数组。
  • 用于Array.prototype.reduce()将数组映射到对象。
  • 用于mapFn映射对象的键和值Array.prototype.push()并将映射的值添加到数组。
const listify = (obj, mapFn) =>
  Object.entries(obj).reduce((acc, [key, value]) => {
    acc.push(mapFn(key, value));
    return acc;
  }, []);

const people = { John: { age: 42 }, Adam: { age: 39 } };
listify(people, (key, value) => ({ name: key, ...value }));
// [ { name: 'John', age: 42 }, { name: 'Adam', age: 39 } ]

特定底数的对数

计算给定基数中给定数字的对数。

  • 用于Math.log()从值和底数中获取对数并将它们相除。
const logBase = (n, base) => Math.log(n) / Math.log(base);
logBase(10, 10); // 1
logBase(100, 10); // 2

数组中最长的元素

获取任意数量的可迭代对象或具有length属性的对象并返回最长的一个。

  • 使用Array.prototype.reduce(), 比较对象的长度以找到最长的对象。
  • 如果多个对象的长度相同,则返回第一个。
  • undefined如果没有提供参数则返回。
const longestItem = (...vals) =>
  vals.reduce((a, x) => (x.length > a.length ? x : a));
longestItem('this', 'is', 'a', 'testcase'); // 'testcase'
longestItem(...['a', 'ab', 'abc']); // 'abc'
longestItem(...['a', 'ab', 'abc'], 'abcd'); // 'abcd'
longestItem([1, 2, 3], [1, 2], [1, 2, 3, 4, 5]); // [1, 2, 3, 4, 5]
longestItem([1, 2, 3], 'foobar'); // 'foobar'

小写对象键

从指定对象创建一个新对象,其中所有键均为小写。

  • 使用Object.keys()andArray.prototype.reduce()从指定对象创建新对象。
  • 将原始对象中的每个键转换为小写,使用String.prototype.toLowerCase().
const lowercaseKeys = obj =>
  Object.keys(obj).reduce((acc, key) => {
    acc[key.toLowerCase()] = obj[key];
    return acc;
  }, {});
const myObj = { Name: 'Adam', sUrnAME: 'Smith' };
const myObjLower = lowercaseKeys(myObj); // {name: 'Adam', surname: 'Smith'};

小写对象键

将对象的所有键转换为小写。

  • 用于Object.keys()获取对象键的数组。
  • 用于Array.prototype.reduce()将数组映射到对象,用于String.prototype.toLowerCase()小写键。
const lowerize = obj =>
  Object.keys(obj).reduce((acc, k) => {
    acc[k.toLowerCase()] = obj[k];
    return acc;
  }, {});
lowerize({ Name: 'John', Age: 22 }); // { name: 'John', age: 22 }

卢恩算法

实施Luhn 算法,用于验证各种识别码,例如信用卡号、IMEI 号、国家提供商标识符号等。

  • 将 String.prototype.split()、Array.prototype.reverse() 和 Array.prototype.map() 与 parseInt() 结合使用以获得数字数组。
  • Array.prototype.shift()获取最后一位数字。
  • Array.prototype.reduce()实现 Luhn 算法。
  • 如果 sum 可以被 10 整除则返回 true,否则返回 false。
const luhnCheck = num => {
  const arr = (num + '')
    .split('')
    .reverse()
    .map(x => parseInt(x));
  const lastDigit = arr.shift();
  let sum = arr.reduce(
    (acc, val, i) => (i % 2 !== 0 ? acc + val : acc + ((val *= 2) > 9 ? val - 9 : val)),
    0
  );
  sum += lastDigit;
  return sum % 10 === 0;
};
luhnCheck('4485275742308327'); // true
luhnCheck(6011329933655299); //  true
luhnCheck(123456789); // false

映射连续元素

使用给定函数 fn 映射每个包含 n 个连续元素的块。
  • 使用 Array.prototype.slice() 获取从左侧移除 n 个元素的 arr。
  • 使用 Array.prototype.map() 和 Array.prototype.slice() 将 fn 应用于 arr 中 n 个连续元素的每个块。
const mapConsecutive = (arr, n, fn) =>
  arr.slice(n - 1).map((v, i) => fn(arr.slice(i, i + n)));
mapConsecutive([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 3, x => x.join('-'));
// ['1-2-3', '2-3-4', '3-4-5', '4-5-6', '5-6-7', '6-7-8', '7-8-9', '8-9-10'];

映射对象键

使用提供的函数映射对象的键,生成一个新对象。

  • 用于Object.keys()迭代对象的键。
  • 用于Array.prototype.reduce()创建具有相同值和映射键的新对象fn
const mapKeys = (obj, fn) =>
  Object.keys(obj).reduce((acc, k) => {
    acc[fn(obj[k], k, obj)] = obj[k];
    return acc;
  }, {});
mapKeys({ a: 1, b: 2 }, (val, key) => key + val); // { a1: 1, b2: 2 }

将数字映射到范围

将数字从一个范围映射到另一个范围。

  • 返回num映射在outMin-outMax来自inMin-之间inMax
const mapNumRange = (num, inMin, inMax, outMin, outMax) =>
  ((num - inMin) * (outMax - outMin)) / (inMax - inMin) + outMin;
mapNumRange(5, 0, 10, 0, 100); // 50

将数组映射到对象

使用函数将数组的值映射到对象。

  • 使用 Array.prototype.reduce() 将 fn 应用于 arr 中的每个元素并将结果组合到一个对象中。
  • 使用 el 作为每个属性的键,使用 fn 的结果作为值。
const mapObject = (arr, fn) =>
  arr.reduce((acc, el, i) => {
    acc[el] = fn(el, i, arr);
    return acc;
  }, {});
mapObject([1, 2, 3], a => a * a); // { 1: 1, 2: 4, 3: 9 }

映射字符串

使用对给定字符串中的每个字符调用提供的函数的结果创建一个新字符串。

  • 使用 String.prototype.split() 和 Array.prototype.map() 为 str 中的每个字符调用提供的函数 fn。
  • 使用 Array.prototype.join() 将字符数组重新组合成一个字符串。
  • 回调函数 fn 接受三个参数(当前字符、当前字符的索引和调用的字符串 mapString)。
const mapString = (str, fn) =>
  str
    .split('')
    .map((c, i) => fn(c, i, str))
    .join('');

mapString('lorem ipsum', c => c.toUpperCase()); // 'LOREM IPSUM'

将map转换为对象

将 Map 转换为对象。

  • 使用 Map.prototype.entries() 将 Map 转换为键值对数组。
  • 使用 Object.fromEntries() 将数组转换为对象。
const mapToObject = map => Object.fromEntries(map.entries());
mapToObject(new Map([['a', 1], ['b', 2]])); // {a: 1, b: 2}

映射对象值

使用提供的函数映射对象的值,生成具有相同键的新对象。

  • Object.keys()迭代对象的键。
  • 使用 Array.prototype.reduce() 使用 fn 创建一个具有相同键和映射值的新对象。
const mapValues = (obj, fn) =>
  Object.keys(obj).reduce((acc, k) => {
    acc[k] = fn(obj[k], k, obj);
    return acc;
  }, {});
const users = {
  fred: { user: 'fred', age: 40 },
  pebbles: { user: 'pebbles', age: 1 }
};
mapValues(users, u => u.age); // { fred: 40, pebbles: 1 }

屏蔽一个值

用指定的掩码字符替换除最后几个字符之外的所有字符。
  • 使用 String.prototype.slice() 获取将保持未屏蔽的字符部分。
  • 使用 String.prototype.padStart() 将字符串的开头填充到原始长度的掩码字符。
  • 如果 num 为负数,则未屏蔽的字符将位于字符串的开头。
  • 省略第二个参数 num,以保留 4 个未屏蔽的默认字符。
  • 省略第三个参数 mask,以使用默认字符“*”作为掩码。
const mask = (cc, num = 4, mask = '*') =>
  `${cc}`.slice(-num).padStart(`${cc}`.length, mask);
mask(1234567890); // '******7890'
mask(1234567890, 3); // '*******890'
mask(1234567890, -4, '$'); // '$$$$567890'

匹配对象属性

比较两个对象以确定第一个对象是否包含与第二个对象等效的属性值。

  • 用于Object.keys()获取第二个对象的所有键。
  • 使用Array.prototype.every(),Object.prototype.hasOwnProperty()和 严格比较来确定所有键是否存在于第一个对象中并且具有相同的值。
const matches = (obj, source) =>
  Object.keys(source).every(
    key => obj.hasOwnProperty(key) && obj[key] === source[key]
  );
matches({ age: 25, hair: 'long', beard: true }, { hair: 'long', beard: true });
// true
matches({ hair: 'long', beard: true }, { age: 25, hair: 'long', beard: true });
// false

根据功能匹配对象属性

根据提供的函数比较两个对象以确定第一个对象是否包含与第二个对象等效的属性值。

  • 用于Object.keys()获取第二个对象的所有键。
  • 使用Array.prototype.every(),Object.prototype.hasOwnProperty()和提供的函数来确定第一个对象中是否存在所有键并具有等效值。
  • 如果未提供函数,将使用相等运算符比较值。
const matchesWith = (obj, source, fn) =>
  Object.keys(source).every(key =>
    obj.hasOwnProperty(key) && fn
      ? fn(obj[key], source[key], key, obj, source)
      : obj[key] == source[key]
  );
const isGreeting = val => /^h(?:i|ello)$/.test(val);
matchesWith(
  { greeting: 'hello' },
  { greeting: 'hi' },
  (oV, sV) => isGreeting(oV) && isGreeting(sV)
); // true

基于函数的数组最大值

在使用提供的函数将每个元素映射到一个值后,返回数组的最大值。

  • 使用 Array.prototype.map() 将每个元素映射到 fn 返回的值。
  • 用于Math.max()获取最大值。
const maxBy = (arr, fn) =>
  Math.max(...arr.map(typeof fn === 'function' ? fn : val => val[fn]));

maxBy([{ n: 4 }, { n: 2 }, { n: 8 }, { n: 6 }], x => x.n); // 8
maxBy([{ n: 4 }, { n: 2 }, { n: 8 }, { n: 6 }], 'n'); // 8

最大日期

返回给定日期的最大值。

  • 使用 ES6 扩展语法 withMath.max()查找最大日期值。
  • 使用Date构造函数将其转换为Date对象。
const maxDate = (...dates) => new Date(Math.max(...dates));
const dates = [
  new Date(2017, 4, 13),
  new Date(2018, 2, 12),
  new Date(2016, 0, 10),
  new Date(2016, 0, 9)
];
maxDate(...dates); // 2018-03-11T22:00:00.000Z

N 个最大元素

从提供的数组中返回 n 个最大元素。
  • 使用 Array.prototype.sort() 结合扩展运算符 (...) 创建数组的浅表克隆并按降序对其进行排序。
  • 用于Array.prototype.slice()获取指定数量的元素。
  • 省略第二个参数 n 以获得单元素数组。
  • 如果n大于或等于提供的数组长度,则返回原始数组(按降序排序)。
const maxN = (arr, n = 1) => [...arr].sort((a, b) => b - a).slice(0, n);
maxN([1, 2, 3]); // [3]
maxN([1, 2, 3], 2); // [3, 2]

最大子数组

在数字数组中查找具有最大总和的连续子数组。

  • 使用贪婪的方法来跟踪当前总和和当前最大值 maxSum。 如果所有值都是负数,则将 maxSum 设置为 -Infinity 以确保返回最大的负值。
  • 定义变量以跟踪最大起始索引 sMax、最大结束索引 eMax 和当前起始索引 s。
  • 使用 Array.prototype.forEach() 迭代值并将当前值添加到总和中。
  • 如果当前总和大于 maxSum,则更新索引值和 maxSum。
  • 如果总和低于 0,则将其重置为 0 并将 s 的值更新为下一个索引。
  • 使用 Array.prototype.slice() 返回索引变量指示的子数组。
const maxSubarray = (...arr) => {
  let maxSum = -Infinity,
    sum = 0;
  let sMax = 0,
    eMax = arr.length - 1,
    s = 0;

  arr.forEach((n, i) => {
    sum += n;
    if (maxSum < sum) {
      maxSum = sum;
      sMax = s;
      eMax = i;
    }

    if (sum < 0) {
      sum = 0;
      s = i + 1;
    }
  });

  return arr.slice(sMax, eMax + 1);
};
maxSubarray(-2, 1, -3, 4, -1, 2, 1, -5, 4); // [4, -1, 2, 1]

中位数

计算数字数组的中位数。

  • 找到数组的中间,用于Array.prototype.sort()对值进行排序。
  • 如果是奇数,则返回中点处的数Array.prototype.length,否则返回中间两个数的平均值。
const median = arr => {
  const mid = Math.floor(arr.length / 2),
    nums = [...arr].sort((a, b) => a - b);
  return arr.length % 2 !== 0 ? nums[mid] : (nums[mid - 1] + nums[mid]) / 2;
};
median([5, 6, 50, 1, -5]); // 5

记忆功能

返回记忆(缓存)函数。

  • 通过实例化一个新的 Map 对象来创建一个空缓存。
  • 通过首先检查该特定输入值的函数输出是否已被缓存,或者如果没有,则存储并返回它,返回一个函数,该函数将单个参数提供给记忆函数。
  • 必须使用 function 关键字以允许记忆函数在必要时更改其 this 上下文。
  • 通过将缓存设置为返回函数的属性来允许访问缓存。
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;
};
// 请参阅“anagrams”片段。
const anagramsCached = memoize(anagrams);
anagramsCached('javascript'); // 花费很长时间
anagramsCached('javascript'); // 几乎立即返回,因为它被缓存了
console.log(anagramsCached.cache); // 缓存的anagrams map

合并对象

从两个或多个对象的组合中创建一个新对象。

  • 使用 Array.prototype.reduce() 结合 Object.keys() 迭代所有对象和键。
  • 使用 Object.prototype.hasOwnProperty() 和 Array.prototype.concat() 为存在于多个对象中的键附加值。
const merge = (...objs) =>
  [...objs].reduce(
    (acc, obj) =>
      Object.keys(obj).reduce((a, k) => {
        acc[k] = acc.hasOwnProperty(k)
          ? [].concat(acc[k]).concat(obj[k])
          : obj[k];
        return acc;
      }, {}),
    {}
  );

const object = {
  a: [{ x: 2 }, { y: 4 }],
  b: 1
};
const other = {
  a: { z: 3 },
  b: [2, 3],
  c: 'foo'
};
merge(object, other);
// { a: [ { x: 2 }, { y: 4 }, { z: 3 } ], b: [ 1, 2, 3 ], c: 'foo' }

合并排序算法

使用合并排序算法对数字数组进行排序。

  • 使用递归。
  • 如果length数组的 小于2,则返回数组。
  • Math.floor()计算数组的中点。
  • Array.prototype.slice()将数组一分为二并递归调用mergeSort()创建的子数组。
  • 最后,使用Array.from()Array.prototype.shift()将两个排序后的子数组合二为一。
const mergeSort = arr => {
  if (arr.length < 2) return arr;
  const mid = Math.floor(arr.length / 2);
  const l = mergeSort(arr.slice(0, mid));
  const r = mergeSort(arr.slice(mid, arr.length));
  return Array.from({ length: l.length + r.length }, () => {
    if (!l.length) return r.shift();
    else if (!r.length) return l.shift();
    else return l[0] > r[0] ? r.shift() : l.shift();
  });
};
mergeSort([5, 1, 4, 2, 3]); // [1, 2, 3, 4, 5]

合并排序数组

将两个排序数组合并为一个。

  • 使用扩展运算符 ( ...) 克隆两个给定的数组。
  • Array.from()根据给定数组创建适当长度的数组。
  • Array.prototype.shift()从克隆数组中删除的元素填充新创建的数组。
const mergeSortedArrays = (a, b) => {
  const _a = [...a],
    _b = [...b];
  return Array.from({ length: _a.length + _b.length }, () => {
    if (!_a.length) return _b.shift();
    else if (!_b.length) return _a.shift();
    else return _a[0] > _b[0] ? _b.shift() : _a.shift();
  });
};

mergeSortedArrays([1, 4, 5], [2, 3, 6]); // [1, 2, 3, 4, 5, 6]

中点

计算两对 (x,y) 点之间的中点。

  • 解构数组以获取x1y1x2y2
  • 通过将两个端点之和除以 来计算每个维度的中点2
const midpoint = ([x1, y1], [x2, y2]) => [(x1 + x2) / 2, (y1 + y2) / 2];
midpoint([2, 2], [4, 4]); // [3, 3]
midpoint([4, 4], [6, 6]); // [5, 5]
midpoint([1, 3], [2, 4]); // [1.5, 3.5]

英里换算为公里

将英里转换为公里。

  • 遵循转换公式km = mi * 1.609344
const milesToKm = miles => miles * 1.609344;
milesToKm(5); // ~8.04672

基于函数的数组最小值

在使用提供的函数将每个元素映射到一个值后,返回数组的最小值。

  • 用于Array.prototype.map()将每个元素映射到 返回的值fn
  • 用于Math.min()获取最小值。
const minBy = (arr, fn) =>
  Math.min(...arr.map(typeof fn === 'function' ? fn : val => val[fn]));
minBy([{ n: 4 }, { n: 2 }, { n: 8 }, { n: 6 }], x => x.n); // 2
minBy([{ n: 4 }, { n: 2 }, { n: 8 }, { n: 6 }], 'n'); // 2

最小日期

返回给定日期的最小值。

  • 使用 ES6 扩展语法 withMath.min()查找最小日期值。
  • 使用Date构造函数将其转换为Date对象。
const minDate = (...dates) => new Date(Math.min(...dates));
const dates = [
  new Date(2017, 4, 13),
  new Date(2018, 2, 12),
  new Date(2016, 0, 10),
  new Date(2016, 0, 9)
];
minDate(...dates); // 2016-01-08T22:00:00.000Z

N 个最小元素

从提供的数组中返回 n 个最小元素。
  • 使用 Array.prototype.sort() 结合扩展运算符 (...) 创建数组的浅表克隆并按升序对其进行排序。
  • 使用 Array.prototype.slice() 获取指定数量的元素。
  • 省略第二个参数 n 以获得单元素数组。
  • 如果 n 大于或等于提供的数组长度,则返回原始数组(按升序排序)。
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]

数组中出现次数最多的元素

返回数组中出现次数最多的元素。

  • 使用 Array.prototype.reduce() 将唯一值映射到对象的键,每次遇到相同的值时添加到现有键。
  • 在结果上结合使用 Object.entries() 和 Array.prototype.reduce() 来获取数组中出现频率最高的值。
const mostFrequent = arr =>
  Object.entries(
    arr.reduce((a, v) => {
      a[v] = a[v] ? a[v] + 1 : 1;
      return a;
    }, {})
  ).reduce((a, v) => (v[1] >= a[1] ? v : a), [null, 0])[0];
mostFrequent(['a', 'b', 'a', 'c', 'a', 'a', 'b']); // 'a'

最高效的函数

返回执行最快的函数在函数数组中的索引。

  • 使用 Array.prototype.map() 生成一个数组,其中每个值都是在迭代次数之后执行该函数所花费的总时间。
  • 使用前后 performance.now() 值的差异来获得以毫秒为单位的高度准确的总时间。
  • 使用 Math.min() 找到最短执行时间,并返回对应于最高性能函数索引的最短时间的索引。
  • 省略第二个参数迭代次数,以使用默认的 10000 次迭代次数。
  • 迭代次数越多,结果越可靠,但所需时间也越长。
const mostPerformant = (fns, iterations = 10000) => {
  const times = fns.map(fn => {
    const before = performance.now();
    for (let i = 0; i < iterations; i++) fn();
    return performance.now() - before;
  });
  return times.indexOf(Math.min(...times));
};
mostPerformant([
  () => {
    // 在返回 `false` 之前循环遍历整个数组
    [1, 2, 3, 4, 5, 6, 7, 8, 9, '10'].every(el => typeof el === 'number');
  },
  () => {
    // 在返回 `false` 之前只需要达到索引 `1`
    [1, '2', 3, 4, 5, 6, 7, 8, 9, 10].every(el => typeof el === 'number');
  }
]); // 1

否定谓词函数

否定谓词函数。

  • 获取一个谓词函数,并将非运算符 ( !) 与其参数一起应用于它。
const negate = func => (...args) => !func(...args);
[1, 2, 3, 4, 5, 6].filter(negate(n => n % 2 === 0)); // [ 1, 3, 5 ]

嵌套对象

在平面数组中递归嵌套相互链接的对象。

  • 使用递归。
  • 使用 Array.prototype.filter() 过滤 id 与链接匹配的项目。
  • 使用 Array.prototype.map() 将每个项目映射到一个新对象,该对象具有 children 属性,该属性根据哪些项目是当前项目的子项递归嵌套项目。
  • 省略第二个参数 id,默认为 null,表示该对象未链接到另一个对象(即它是顶级对象)。
  • 省略第三个参数 link,以使用“parent_id”作为默认属性,通过其 id 将对象链接到另一个对象。
const nest = (items, id = null, link = 'parent_id') =>
  items
    .filter(item => item[link] === id)
    .map(item => ({ ...item, children: nest(items, item.id, link) }));
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: [...] }]

节点列表转数组

将 NodeList 转换为数组。

  • 在新数组中使用扩展运算符 (...) 将 NodeList 转换为数组。
const nodeListToArray = nodeList => [...nodeList];
nodeListToArray(document.childNodes); // [ <!DOCTYPE html>, html ]

测试所有数组元素是否为假

检查提供的谓词函数是否返回false集合中的所有元素。

  • 用于Array.prototype.some()测试集合中是否有任何元素true基于返回fn
  • 省略第二个参数 ,fn用作Boolean默认值。
const none = (arr, fn = Boolean) => !arr.some(fn);
none([0, 1, 3, 0], x => x == 2); // true
none([0, 0, 0]); // true

逻辑或

检查是否没有参数是true.

  • 使用逻辑非 (!) 运算符返回两个给定值的逻辑或 (||) 的倒数。
const nor = (a, b) => !(a||b);
nor(true, true); // false
nor(true, false); // false
nor(false, false); // true

规范化行尾

规范化字符串中的行结尾。

  • 使用 String.prototype.replace() 和正则表达式来匹配和替换行结尾为normalized。
  • 省略第二个参数,normalized使用默认值 '\r\n'。
const normalizeLineEndings = (str, normalized = '\r\n') =>
  str.replace(/\r?\n/g, normalized);
normalizeLineEndings('This\r\nis a\nmultiline\nstring.\r\n');
// 'This\r\nis a\r\nmultiline\r\nstring.\r\n'
normalizeLineEndings('This\r\nis a\nmultiline\nstring.\r\n', '\n');
// 'This\nis a\nmultiline\nstring.\n'

逻辑非

返回给定值的逻辑逆。

  • 使用逻辑非 ( !) 运算符返回给定值的倒数。
const not = a => !a;
not(true); // false
not(false); // true

第 N 个参数

创建一个获取索引 n 处的参数的函数。

  • 使用 Array.prototype.slice() 在索引 n 处获取所需的参数。
  • 如果n为负数,则返回倒数第 n 个参数。
const nthArg = n => (...args) => args.slice(n)[0];
const third = nthArg(2);
third(1, 2, 3); // 3
third(1, 2); // undefined
const last = nthArg(-1);
last(1, 2, 3, 4, 5); // 5

数的 N 次方根

计算给定数字的 n 次方根。

  • 用于Math.pow()计算等于 的 n 次方根的x次方。1 / n`x`
const nthRoot = (x, n) => Math.pow(x, 1 / n);

nthRoot(32, 5); // 2

二维数组转对象

从给定的键值对创建一个对象。

  • 用于Array.prototype.reduce()创建和组合键值对。
  • Object.fromEntries()提供类似的功能。
const objectFromPairs = arr =>
  arr.reduce((a, [key, val]) => ((a[key] = val), a), {});
objectFromPairs([['a', 1], ['b', 2]]); // {a: 1, b: 2}

对象转二维数组

从一个对象创建一个键值对数组。

  • 使用Object.keys()andArray.prototype.map()迭代对象的键并生成包含键值对的数组。
  • Object.entries()提供类似的功能。
const objectToEntries = obj => Object.keys(obj).map(k => [k, obj[k]]);
objectToEntries({ a: 1, b: 2 }); // [ ['a', 1], ['b', 2] ]

将对象转换为Map

将对象转换为Map.

  • Object.entries()将对象转换为键值对数组。
  • 使用Map构造函数将数组转换为Map.
const objectToMap = obj => new Map(Object.entries(obj));
objectToMap({a: 1, b: 2}); // Map {'a' => 1, 'b' => 2}

对象转二维数组

从一个对象创建一个键值对数组。

  • 用于Object.entries()从给定对象中获取键值对数组。
const objectToPairs = obj => Object.entries(obj);
objectToPairs({ a: 1, b: 2 }); // [ ['a', 1], ['b', 2] ]

对象转查询字符串

从给定对象的键值对生成查询字符串。

  • 在 Object.entries() 上使用 Array.prototype.reduce() 从 queryParameters 创建查询字符串。
  • 确定符号是 ? 或 & 基于 queryString 的长度。
  • 仅当它是字符串时才将 val 连接到 queryString。
  • 当 queryParameters 为假时,返回 queryString 或空字符串。
const objectToQueryString = queryParameters => {
  return queryParameters
    ? Object.entries(queryParameters).reduce(
        (queryString, [key, val], index) => {
          const symbol = queryString.length === 0 ? '?' : '&';
          queryString +=
            typeof val === 'string' ? `${symbol}${key}=${val}` : '';
          return queryString;
        },
        ''
      )
    : '';
};
objectToQueryString({ page: '1', size: '2kg', key: undefined });
// '?page=1&size=2kg'

将数组映射成对象

使用提供的映射函数将对象数组映射到对象。

  • 使用 Array.prototype.reduce() 将数组映射到对象。
  • 使用 mapKey 映射对象的键,使用 mapValue 映射值。
const objectify = (arr, mapKey, mapValue = i => i) =>
  arr.reduce((acc, item) => {
    acc[mapKey(item)] = mapValue(item);
    return acc;
  }, {});
const people = [ { name: 'John', age: 42 }, { name: 'Adam', age: 39 } ];
objectify(people, p => p.name.toLowerCase());
// { john: { name: 'John', age: 42 }, adam: { name: 'Adam', age: 39 } }
objectify(
  people,
  p => p.name.toLowerCase(),
  p => p.age
);
// { john: 42, adam: 39 }

观察元素变化

创建一个新的 MutationObserver 并为指定元素上的每个突变运行提供的回调。

  • 使用 MutationObserver观察给定元素的变化。。
  • 使用 Array.prototype.forEach() 为观察到的每个突变运行回调。
  • 省略第三个参数 ,以使用默认选项(全部为真)。
const observeMutations = (element, callback, options) => {
  const observer = new MutationObserver(mutations =>
    mutations.forEach(m => callback(m))
  );
  observer.observe(
    element,
    Object.assign(
      {
        childList: true,
        attributes: true,
        attributeOldValue: true,
        characterData: true,
        characterDataOldValue: true,
        subtree: true,
      },
      options
    )
  );
  return observer;
};
const obs = observeMutations(document, console.log);
// 记录页面上发生的所有变化
obs.disconnect();
// 断开观察者并停止在页面上记录突变

从元素中移除事件监听器

从元素中移除事件侦听器。

  • 使用 EventTarget.removeEventListener() 从元素中移除事件侦听器。
  • 省略第四个参数选择使用 false 或根据添加事件侦听器时使用的选项指定它。
const off = (el, evt, fn, opts = false) =>
  el.removeEventListener(evt, fn, opts);
const fn = () => console.log('!');
document.body.addEventListener('click', fn);
off(document.body, 'click', fn); // 不再记录 '!' 点击页面后

偏移数组元素

将指定数量的元素移动到数组的末尾。

  • 使用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]

省略对象属性

省略与对象中给定键对应的键值对。

  • 使用Object.keys(),Array.prototype.filter()Array.prototype.includes()删除提供的密钥。
  • Array.prototype.reduce()将过滤后的键转换回具有相应键值对的对象。
const omit = (obj, arr) =>
  Object.keys(obj)
    .filter(k => !arr.includes(k))
    .reduce((acc, key) => ((acc[key] = obj[key]), acc), {});
omit({ a: 1, b: '2', c: 3 }, ['b']); // { 'a': 1, 'c': 3 }

根据功能省略对象属性

省略与给定函数返回 falsy 的对象的键对应的键值对。

  • 使用Object.keys()and删除返回真值Array.prototype.filter()的键。fn
  • Array.prototype.reduce()将过滤后的键转换回具有相应键值对的对象。
  • 使用两个参数调用回调函数:(value, key)。
const omitBy = (obj, fn) =>
  Object.keys(obj)
    .filter(k => !fn(obj[k], k))
    .reduce((acc, key) => ((acc[key] = obj[key]), acc), {});

omitBy({ a: 1, b: '2', c: 3 }, x => typeof x === 'number'); // { b: '2' }

向元素添加事件监听器

将事件侦听器添加到能够使用事件委托的元素。

  • EventTarget.addEventListener()向元素添加事件侦听器。
  • 如果向选项对象提供了目标属性,请确保事件目标与指定的目标相匹配,然后通过提供正确的 this 上下文来调用回调。
  • 省略opts默认为非委托行为和事件冒泡。
  • 返回对自定义委托函数的引用,以便可以与 一起使用off
const on = (el, evt, fn, opts = {}) => {
  const delegatorFn = e =>
    e.target.matches(opts.target) && fn.call(e.target, e);
  el.addEventListener(
    evt,
    opts.target ? delegatorFn : fn,
    opts.options || false
  );
  if (opts.target) return delegatorFn;
};

const fn = () => console.log('!');
on(document.body, 'click', fn); // 记录'!' 点击body
on(document.body, 'click', fn, { target: 'p' });
// 记录'!' 单击 body 的 `p` 子元素后
on(document.body, 'click', fn, { options: true });
// 使用捕获而不是冒泡

处理外部点击

每当用户在指定元素之外单击时运行回调。

  • EventTarget.addEventListener()监听'click'事件。
  • Node.contains()检查是否Event.target是的后代,如果不是则element运行callback
const onClickOutside = (element, callback) => {
  document.addEventListener('click', e => {
    if (!element.contains(e.target)) callback();
  });
};

onClickOutside('#my-element', () => console.log('Hello'));
// 每当用户在#my-element 之外单击时都会记录“你好”

处理滚动停止

每当用户停止滚动时运行回调。

  • EventTarget.addEventListener()监听事件'scroll'
  • setTimeout()等待150ms 直到调用给定的callback.
  • 如果在 ms内触发了新事件,则用于clearTimeout()清除超时。'scroll'`150`
const onScrollStop = callback => {
  let isScrolling;
  window.addEventListener(
    'scroll',
    e => {
      clearTimeout(isScrolling);
      isScrolling = setTimeout(() => {
        callback();
      }, 150);
    },
    false
  );
};
onScrollStop(() => {
  console.log('The user has stopped scrolling');
});

处理用户输入变化

每当用户输入类型发生变化(鼠标或触摸)时运行回调。
  • 使用两个事件侦听器。
  • 假设最初是鼠标输入,并将“touchstart”事件侦听器绑定到文档。
  • 在“touchstart”上,使用 performance.now() 添加一个“mousemove”事件侦听器以侦听在 20 毫秒内触发的两个连续“mousemove”事件。
  • 在任何一种情况下,以输入类型作为参数运行回调。
const onUserInputChange = callback => {
  let type = 'mouse',
    lastTime = 0;
  const mousemoveHandler = () => {
    const now = performance.now();
    if (now - lastTime < 20)
      (type = 'mouse'),
        callback(type),
        document.removeEventListener('mousemove', mousemoveHandler);
    lastTime = now;
  };
  document.addEventListener('touchstart', () => {
    if (type === 'touch') return;
    (type = 'touch'),
      callback(type),
      document.addEventListener('mousemove', mousemoveHandler);
  });
};
onUserInputChange(type => {
  console.log('The user is now using', type, 'as an input method.');
});

调用函数一次

确保一个函数只被调用一次。

  • 利用闭包,使用标志,调用,并在第一次调用该函数后将其设置为 true,以防止再次调用它。
  • 为了允许函数更改其 this 上下文(例如在事件侦听器中),必须使用 function 关键字,并且提供的函数必须应用上下文。
  • 允许使用 rest/spread (...) 运算符为函数提供任意数量的参数。
const once = fn => {
  let called = false;
  return function(...args) {
    if (called) return;
    called = true;
    return fn.apply(this, args);
  };
};

const startApp = function(event) {
  console.log(this, event); // document.body, 鼠标事件
};
document.body.addEventListener('click', once(startApp));
// 仅在单击时运行 `startApp` 一次

逻辑或

检查至少一个参数是否为真。

  • 对两个给定值使用逻辑或 (||) 运算符。
const or = (a, b) => a || b;
or(true, true); // true
or(true, false); // true
or(false, false); // false

排序对象数组

对对象数组进行排序,按属性和顺序排序。

  • 在 props 数组上使用 Array.prototype.sort()、Array.prototype.reduce(),默认值为 0。
  • 根据提供的顺序使用数组解构来交换属性位置。
  • 如果未提供订单数组,则默认按“asc”排序。
const orderBy = (arr, props, orders) =>
  [...arr].sort((a, b) =>
    props.reduce((acc, prop, i) => {
      if (acc === 0) {
        const [p1, p2] =
          orders && orders[i] === 'desc'
            ? [b[prop], a[prop]]
            : [a[prop], b[prop]];
        acc = p1 > p2 ? 1 : p1 < p2 ? -1 : 0;
      }
      return acc;
    }, 0)
  );
const users = [
  { name: 'fred', age: 48 },
  { name: 'barney', age: 36 },
  { name: 'fred', age: 40 },
];
orderBy(users, ['name', 'age'], ['asc', 'desc']);
// [{name: 'barney', age: 36}, {name: 'fred', age: 48}, {name: 'fred', age: 40}]
orderBy(users, ['name', 'age']);
// [{name: 'barney', age: 36}, {name: 'fred', age: 40}, {name: 'fred', age: 48}]

根据属性顺序排列对象数组

根据提供的顺序数组,按属性对对象数组进行排序。

  • 使用 Array.prototype.reduce() 从顺序数组创建一个对象,其中值作为键,它们的原始索引作为值。
  • 使用 Array.prototype.sort() 对给定数组进行排序,跳过 order 数组中 prop 为空或不为空的元素。
const orderWith = (arr, prop, order) => {
  const orderValues = order.reduce((acc, v, i) => {
    acc[v] = i;
    return acc;
  }, {});
  return [...arr].sort((a, b) => {
    if (orderValues[a[prop]] === undefined) return 1;
    if (orderValues[b[prop]] === undefined) return -1;
    return orderValues[a[prop]] - orderValues[b[prop]];
  });
};
const users = [
  { name: 'fred', language: 'Javascript' },
  { name: 'barney', language: 'TypeScript' },
  { name: 'frannie', language: 'Javascript' },
  { name: 'anna', language: 'Java' },
  { name: 'jimmy' },
  { name: 'nicky', language: 'Python' },
];
orderWith(users, 'language', ['Javascript', 'TypeScript', 'Java']);
/*
[
  { name: 'fred', language: 'Javascript' },
  { name: 'frannie', language: 'Javascript' },
  { name: 'barney', language: 'TypeScript' },
  { name: 'anna', language: 'Java' },
  { name: 'jimmy' },
  { name: 'nicky', language: 'Python' }
]
*/

在参数上调用函数

创建一个函数,该函数使用它接收的参数调用每个提供的函数并返回结果。

  • 使用Array.prototype.map()和`Function.prototype.apply()`将每个函数应用于给定的参数。
const over = (...fns) => (...args) => fns.map(fn => fn.apply(null, args));
const minMax = over(Math.min, Math.max);
minMax(1, 2, 3, 4, 5); // [1, 5]

转换函数参数

创建一个函数,该函数调用提供的函数并转换其参数。

  • 使用 Array.prototype.map() 将转换应用于 args 并结合扩展运算符 (...) 将转换后的参数传递给 fn。
const overArgs = (fn, transforms) =>
  (...args) => fn(...args.map((val, i) => transforms[i](val)));
const square = n => n * n;
const double = n => n * 2;
const fn = overArgs((x, y) => [x, y], [square, double]);
fn(9, 3); // [81, 6]

填充字符串

如果字符串短于指定长度,则用指定字符在其两侧填充字符串。

  • 使用String.prototype.padStart()和`String.prototype.padEnd()`填充给定字符串的两边。
  • 省略第三个参数,char使用空白字符作为默认填充字符。
const pad = (str, length, char = ' ') =>
  str.padStart((str.length + length) / 2, char).padEnd(length, char);
pad('cat', 8); // '  cat   '
pad(String(42), 6, '0'); // '004200'
pad('foobar', 3); // 'foobar'

数字补零

将给定数字填充到指定长度。

  • String.prototype.padStart()在将数字转换为字符串后,用于将数字填充到指定长度。
const padNumber = (n, l) => `${n}`.padStart(l, '0');
padNumber(1234, 6); // '001234'

回文串

检查给定的字符串是否为回文。

  • 将字符串规范化为String.prototype.toLowerCase()并用于String.prototype.replace()从中删除非字母数字字符。
  • 使用扩展运算符 ( ...) 将规范化字符串拆分为单个字符。
  • 使用Array.prototype.reverse(),Array.prototype.join()并将结果与规范化字符串进行比较。
const palindrome = str => {
  const s = str.toLowerCase().replace(/[\W_]/g, '');
  return s === [...s].reverse().join('');
};

palindrome('taco cat'); // true

解析cookie

解析 HTTP Cookie 标头字符串,返回所有 cookie 名称-值对的对象。

  • 用于String.prototype.split()将键值对彼此分开。
  • 使用Array.prototype.map()和`String.prototype.split()`将每对中的键与值分开。
  • 使用Array.prototype.reduce()和`decodeURIComponent()`创建一个包含所有键值对的对象。
const parseCookie = str =>
  str
    .split(';')
    .map(v => v.split('='))
    .reduce((acc, v) => {
      acc[decodeURIComponent(v[0].trim())] = decodeURIComponent(v[1].trim());
      return acc;
    }, {});
parseCookie('foo=bar; equation=E%3Dmc%5E2');
// { foo: 'bar', equation: 'E=mc^2' }

前置函数参数

创建一个函数,该函数调用 fn 并在其接收的参数前面加上部分。

  • 使用扩展运算符 (...) 将部分添加到 fn 的参数列表中。
const partial = (fn, ...partials) => (...args) => fn(...partials, ...args);

const greet = (greeting, name) => greeting + ' ' + name + '!';
const greetHello = partial(greet, 'Hello');
greetHello('John'); // 'Hello John!'

追加函数参数

创建一个函数,该函数调用 fn 并将部分附加到它接收的参数。
  • 使用扩展运算符 (...) 将部分附加到 fn 的参数列表。
const partialRight = (fn, ...partials) => (...args) => fn(...args, ...partials);
const greet = (greeting, name) => greeting + ' ' + name + '!';
const greetJohn = partialRight(greet, 'John');
greetJohn('Hello'); // 'Hello John!'

分区数组一分为二

根据提供的函数对每个元素的真实性,将元素分组到两个数组中。

  • 用于Array.prototype.reduce()创建两个数组的数组。
  • 使用 Array.prototype.push() 将 fn 返回 true 的元素添加到第一个数组,将 fn 返回 false 的元素添加到第二个数组。
const partition = (arr, fn) =>
  arr.reduce(
    (acc, val, i, arr) => {
      acc[fn(val, i, arr) ? 0 : 1].push(val);
      return acc;
    },
    [[], []]
  );
const users = [
  { user: 'barney', age: 36, active: false },
  { user: 'fred', age: 40, active: true },
];
partition(users, o => o.active);
// [
//   [{ user: 'fred', age: 40, active: true }],
//   [{ user: 'barney', age: 36, active: false }]
// ]

分区数组

适用fn于 中的每个值arr,每次提供的函数返回新值时将其拆分。

  • 将 Array.prototype.reduce() 与一个累加器对象一起使用,该对象将保存结果数组和从 fn 返回的最后一个值。
  • 用 Array.prototype.push() 将 arr 中的每个值添加到累加器数组中的适当分区。
const partitionBy = (arr, fn) =>
  arr.reduce(
    ({ res, last }, v, i, a) => {
      const next = fn(v, i, a);
      if (next !== last) res.push([v]);
      else res[res.length - 1].push(v);
      return { res, last: next };
    },
    { res: [] }
  ).res;
const numbers = [1, 1, 3, 3, 4, 5, 5, 5];
partitionBy(numbers, n => n % 2 === 0); // [[1, 1, 3, 3], [4], [5, 5, 5]]
partitionBy(numbers, n => n); // [[1, 1], [3, 3], [4], [5, 5, 5]]

匹配的百分位数

计算给定数组中小于或等于给定值的数字的百分比。

  • 用于Array.prototype.reduce()计算有多少数字低于该值以及有多少数字与该值相同,并应用百分位数公式。
const percentile = (arr, val) =>
  (100 *
    arr.reduce(
      (acc, v) => acc + (v < val ? 1 : 0) + (v === val ? 0.5 : 0),
      0
    )) /
  arr.length;

percentile([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 6); // 55

数组排列

生成数组元素的所有排列(包含重复项)。

  • 使用递归。
  • 对于给定数组中的每个元素,为其其余元素创建所有部分排列。
  • 用于Array.prototype.map()将元素与每个部分排列组合,然后Array.prototype.reduce()将所有排列组合在一个数组中。
  • 基本情况是Array.prototype.length等于21
  • ⚠️ 警告:此函数的执行时间随每个数组元素呈指数增长。超过 8 到 10 个条目可能会导致浏览器在尝试解决所有不同组合时挂起。
const permutations = arr => {
  if (arr.length <= 2) return arr.length === 2 ? [arr, [arr[1], arr[0]]] : arr;
  return arr.reduce(
    (acc, item, i) =>
      acc.concat(
        permutations([...arr.slice(0, i), ...arr.slice(i + 1)]).map(val => [
          item,
          ...val,
        ])
      ),
    []
  );
};
permutations([1, 33, 5]);
// [ [1, 33, 5], [1, 5, 33], [33, 1, 5], [33, 5, 1], [5, 1, 33], [5, 33, 1] ]

选择对象键

从对象中选择与给定键对应的键值对。

  • 如果对象中存在键,则用于Array.prototype.reduce()将筛选/选择的键转换回具有相应键值对的对象。
const pick = (obj, arr) =>
  arr.reduce((acc, curr) => (curr in obj && (acc[curr] = obj[curr]), acc), {});
pick({ a: 1, b: '2', c: 3 }, ['a', 'c']); // { 'a': 1, 'c': 3 }

选择匹配的对象键

创建一个由给定函数返回 truthy 的属性组成的对象。

  • 使用 Object.keys() 和 Array.prototype.filter() 删除 fn 返回虚假值的键。
  • 使用 Array.prototype.reduce() 将过滤后的键转换回具有相应键值对的对象。
  • 使用两个参数调用回调函数:(value, key)。
const pickBy = (obj, fn) =>
  Object.keys(obj)
    .filter(k => fn(obj[k], k))
    .reduce((acc, key) => ((acc[key] = obj[key]), acc), {});
pickBy({ a: 1, b: '2', c: 3 }, x => typeof x === 'number');
// { 'a': 1, 'c': 3 }

管道异步函数

为异步函数执行从左到右的函数组合。

  • 使用 Array.prototype.reduce() 和扩展运算符 (...) 来使用 Promise.prototype.then() 执行函数组合。
  • 这些函数可以返回正常值、Promises 或异步的组合,通过 await 返回。
  • 所有函数都必须接受一个参数。
const pipeAsyncFunctions = (...fns) =>
  arg => fns.reduce((p, f) => p.then(f), Promise.resolve(arg));
const sum = pipeAsyncFunctions(
  x => x + 1,
  x => new Promise(resolve => setTimeout(() => resolve(x + 2), 1000)),
  x => x + 3,
  async x => (await x) + 4
);
(async() => {
  console.log(await sum(5)); // 15 (after one second)
})();

从对象数组中提取值

将对象数组转换为与指定键对应的值数组。

  • 用于Array.prototype.map()将对象数组映射到key每个对象的值。
const pluck = (arr, key) => arr.map(i => i[key]);

const simpsons = [
  { name: 'lisa', age: 8 },
  { name: 'homer', age: 36 },
  { name: 'marge', age: 34 },
  { name: 'bart', age: 10 }
];
pluck(simpsons, 'age'); // [8, 36, 34, 10]

字符串单词复数

根据输入的数字返回单词的单数或复数形式,如果提供的话,使用可选的字典。

  • 使用闭包定义一个函数,该函数根据 num 的值将给定的单词复数。
  • 如果 num 为 -1 或 1,则返回单词的单数形式。
  • 如果 num 是任何其他数字,则返回复数形式。
  • 省略第三个参数复数,以使用默认的单数词 + s,或在必要时提供自定义复数词。
  • 如果第一个参数是一个对象,则返回一个函数,该函数可以使用提供的字典来解析单词的正确复数形式。
const pluralize = (val, word, plural = word + 's') => {
  const _pluralize = (num, word, plural = word + 's') =>
    [1, -1].includes(Number(num)) ? word : plural;
  if (typeof val === 'object')
    return (num, word) => _pluralize(num, word, val[word]);
  return _pluralize(val, word, plural);
};
pluralize(0, 'apple'); // 'apples'
pluralize(1, 'apple'); // 'apple'
pluralize(2, 'apple'); // 'apples'
pluralize(2, 'person', 'people'); // 'people'

const PLURALS = {
  person: 'people',
  radius: 'radii'
};
const autoPluralize = pluralize(PLURALS);
autoPluralize(2, 'person'); // 'people'

幂集

返回给定数字数组的幂集。

  • 使用 Array.prototype.reduce() 结合 Array.prototype.map() 迭代元素并组合成包含所有组合的数组。
const powerset = arr =>
  arr.reduce((a, v) => a.concat(a.map(r => r.concat(v))), [[]]);
powerset([1, 2]); // [[], [1], [2], [1, 2]]

用户偏好是深色方案

检查用户配色方案偏好是否为深色。

  • 与适当的媒体查询一起使用Window.matchMedia()来检查用户配色方案偏好。
const prefersDarkColorScheme = () =>
  window &&
  window.matchMedia &&
  window.matchMedia('(prefers-color-scheme: dark)').matches;
prefersDarkColorScheme(); // true

用户偏好是浅色方案

检查用户配色方案偏好是否为light.

  • 与适当的媒体查询一起使用Window.matchMedia()来检查用户配色方案偏好。
const prefersLightColorScheme = () =>
  window &&
  window.matchMedia &&
  window.matchMedia('(prefers-color-scheme: light)').matches;
prefersLightColorScheme(); // true

CSS添加浏览器前缀

根据当前浏览器为 CSS 属性添加前缀。

  • 在供应商前缀字符串数组上使用 Array.prototype.findIndex() 来测试 Document.body 是否在其 CSSStyleDeclaration 对象中定义了其中之一,否则返回 null。
  • 使用 String.prototype.charAt() 和 String.prototype.toUpperCase() 将属性大写,这将附加到供应商前缀字符串。
const prefix = prop => {
  const capitalizedProp = prop.charAt(0).toUpperCase() + prop.slice(1);
  const prefixes = ['', 'webkit', 'moz', 'ms', 'o'];
  const i = prefixes.findIndex(
    prefix =>
      typeof document.body.style[prefix ? prefix + capitalizedProp : prop] !==
      'undefined'
  );
  return i !== -1 ? (i === 0 ? prop : prefixes[i] + capitalizedProp) : null;
};
prefix('appearance');
// 在受支持的浏览器上为“appearance”,否则为“webkitAppearance”、“mozAppearance”、“msAppearance”或“oAppearance”

字节数转换

将字节数转换为人类可读的字符串。

  • 使用要根据指数访问的单位数组字典。
  • 用于Number.prototype.toPrecision()将数字截断为特定位数。
  • 通过构建它返回美化的字符串,同时考虑提供的选项以及它是否为负数。
  • 省略第二个参数 ,precision以使用默认的数字精度3
  • 省略第三个参数,addSpace默认情况下在数字和单位之间添加空格。
const prettyBytes = (num, precision = 3, addSpace = true) => {
  const UNITS = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
  if (Math.abs(num) < 1) return num + (addSpace ? ' ' : '') + UNITS[0];
  const exponent = Math.min(
    Math.floor(Math.log10(num < 0 ? -num : num) / 3),
    UNITS.length - 1
  );
  const n = Number(
    ((num < 0 ? -num : num) / 1000 ** exponent).toPrecision(precision)
  );
  return (num < 0 ? '-' : '') + n + (addSpace ? ' ' : '') + UNITS[exponent];
};
prettyBytes(1000); // '1 KB'
prettyBytes(-27145424323.5821, 5); // '-27.145 GB'
prettyBytes(123456789, 3, false); // '123MB'

数的质因数

使用试除算法查找给定数字的质因数。

  • 使用while循环遍历所有可能的质因数,从 开始2
  • 如果当前因子 ,f正好除以n, 添加f到因子数组并n除以f. 否则,加f一
const primeFactors = n => {
  let a = [],
    f = 2;
  while (n > 1) {
    if (n % f === 0) {
      a.push(f);
      n /= f;
    } else {
      f++;
    }
  }
  return a;
};
primeFactors(147); // [3, 7, 7]

素数

使用 Eratosthenes 筛法生成不超过给定数的素数。

  • 生成一个从 2 到给定数字的数组。
  • 使用 Array.prototype.filter() 过滤出可被从 2 到所提供数字的平方根之间的任何数字整除的值。
const primes = num => {
  let arr = Array.from({ length: num - 1 }).map((x, i) => i + 2),
    sqroot = Math.floor(Math.sqrt(num)),
    numsTillSqroot = Array.from({ length: sqroot - 1 }).map((x, i) => i + 2);
  numsTillSqroot.forEach(x => (arr = arr.filter(y => y % x !== 0 || y === x)));
  return arr;
};
primes(10); // [2, 3, 5, 7]

数值的乘积

计算两个或多个数字/数组的乘积。

  • 用于Array.prototype.reduce()将每个值与累加器相乘,累加器初始化为1.
const prod = (...arr) => [...arr].reduce((acc, val) => acc * val, 1);
prod(1, 2, 3, 4); // 24
prod(...[1, 2, 3, 4]); // 24

Promise函数

转换异步函数以返回Promise。

  • 使用柯里化返回一个函数,该函数返回一个Promise调用原始函数的函数。
  • 使用剩余运算符 ( ...) 传递所有参数。
  • 注意:在 Node 8+ 中,您可以使用util.promisify.
const promisify = func => (...args) =>
  new Promise((resolve, reject) =>
    func(...args, (err, result) => (err ? reject(err) : resolve(result)))
  );
const delay = promisify((d, cb) => setTimeout(cb, d));
delay(2000).then(() => console.log('Hi!')); // Promise 在 2 秒后解决

从数组中提取值

改变原始数组以过滤掉指定的值。

  • 使用Array.prototype.filter()和`Array.prototype.includes()`提取不需要的值。
  • 设置Array.prototype.length为通过将数组的长度重置为 来改变传入的数组0
  • 用于Array.prototype.push()仅使用提取的值重新填充它。
const pull = (arr, ...args) => {
  let argState = Array.isArray(args[0]) ? args[0] : args;
  let pulled = arr.filter(v => !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()和`Array.prototype.includes()`提取不需要的值。
  • 设置 Array.prototype.length 以通过将其长度重置为 0 来改变传入的数组。
  • 使用 Array.prototype.push() 仅使用拉取的值重新填充它。
  • Array.prototype.push()跟踪提取的值。
const pullAtIndex = (arr, pullArr) => {
  let removed = [];
  let pulled = arr
    .map((v, i) => (pullArr.includes(i) ? removed.push(v) : v))
    .filter((v, i) => !pullArr.includes(i));
  arr.length = 0;
  pulled.forEach(v => arr.push(v));
  return removed;
};
let myArray = ['a', 'b', 'c', 'd'];
let pulled = pullAtIndex(myArray, [1, 3]);
// myArray = [ 'a', 'c' ] , pulled = [ 'b', 'd' ]

从数组中提取匹配值

改变原始数组以过滤掉指定的值。返回删除的元素。

  • 使用Array.prototype.filter()和`Array.prototype.includes()`提取不需要的值。
  • 设置 Array.prototype.length 以通过将其长度重置为 0 来改变传入的数组。
  • 使用 Array.prototype.push() 仅使用拉取的值重新填充它。
  • 使用 Array.prototype.push() 来跟踪提取的值。
const pullAtValue = (arr, pullArr) => {
  let removed = [],
    pushToRemove = arr.forEach((v, i) =>
      pullArr.includes(v) ? removed.push(v) : v
    ),
    mutateTo = arr.filter((v, i) => !pullArr.includes(v));
  arr.length = 0;
  mutateTo.forEach(v => arr.push(v));
  return removed;
};

let myArray = ['a', 'b', 'c', 'd'];
let pulled = pullAtValue(myArray, ['b', 'd']);
// myArray = [ 'a', 'c' ] , pulled = [ 'b', 'd' ]

根据函数从数组中提取值

根据给定的迭代器函数改变原始数组以过滤掉指定的值。

  • 检查提供的最后一个参数是否是一个函数。
  • 使用 Array.prototype.map() 将迭代器函数 fn 应用于所有数组元素。
  • 使用 Array.prototype.filter() 和 Array.prototype.includes() 提取不需要的值。
  • 设置 Array.prototype.length 以通过将其长度重置为 0 来改变传入的数组。
  • 使用 Array.prototype.push() 仅使用拉取的值重新填充它。
const pullBy = (arr, ...args) => {
  const length = args.length;
  let fn = length > 1 ? args[length - 1] : undefined;
  fn = typeof fn == 'function' ? (args.pop(), fn) : undefined;
  let argState = (Array.isArray(args[0]) ? args[0] : args).map(val => fn(val));
  let pulled = arr.filter((v, i) => !argState.includes(fn(v)));
  arr.length = 0;
  pulled.forEach(v => arr.push(v));
};
var myArray = [{ x: 1 }, { x: 2 }, { x: 3 }, { x: 1 }];
pullBy(myArray, [{ x: 1 }, { x: 3 }], o => o.x); // myArray = [{ x: 2 }]

季度

返回提供的日期所属的季度和年份。

  • 用于Date.prototype.getMonth()获取范围 (0, 11) 中的当前月份,添加1以将其映射到范围 (1, 12)。
  • 使用Math.ceil()并将月份除以3得到当前季度。
  • Date.prototype.getFullYear()从给定的中获取年份date
  • 省略参数 ,date默认使用当前日期。
const quarterOfYear = (date = new Date()) => [
  Math.ceil((date.getMonth() + 1) / 3),
  date.getFullYear()
];

quarterOfYear(new Date('07/10/2018')); // [ 3, 2018 ]
quarterOfYear(); // [ 4, 2020 ]

对象的查询字符串

从给定的查询字符串或 URL 生成一个对象。

  • 使用 String.prototype.split() 从给定的 url 获取参数。
  • 使用 URLSearchParams 构造函数创建适当的对象并使用扩展运算符 (...) 将其转换为键值对数组。
  • 使用 Array.prototype.reduce() 将键值对数组转换为对象。
const queryStringToObject = url =>
  [...new URLSearchParams(url.split('?')[1])].reduce(
    (a, [k, v]) => ((a[k] = v), a),
    {}
  );
queryStringToObject('https://google.com?page=1&count=10');
// {page: '1', count: '10'}

快速排序算法

使用快速排序算法对数字数组进行排序。

  • 使用递归。
  • 使用扩展运算符 ( ...) 克隆原始数组arr.
  • 如果数组的长度小于 2,则返回克隆的数组。
  • 使用 Math.floor() 计算枢轴元素的索引。
  • Array.prototype.push() 将数组拆分为两个子数组。 第一个包含小于或等于 pivot 的元素,第二个包含大于它的元素。 将结果解构为两个数组。
  • 在创建的子数组上递归调用 quickSort()。
const quickSort = arr => {
  const a = [...arr];
  if (a.length < 2) return a;
  const pivotIndex = Math.floor(arr.length / 2);
  const pivot = a[pivotIndex];
  const [lo, hi] = a.reduce(
    (acc, val, i) => {
      if (val < pivot || (val === pivot && i != pivotIndex)) {
        acc[0].push(val);
      } else if (val > pivot) {
        acc[1].push(val);
      }
      return acc;
    },
    [[], []]
  );
  return [...quickSort(lo), pivot, ...quickSort(hi)];
};

quickSort([1, 6, 1, 5, 3, 2, 1, 4]); // [1, 1, 1, 2, 3, 4, 5, 6]

弧度到度

将角度从弧度转换为度数。

  • 使用Math.PI弧度转度公式将角度从弧度转换为度数。
const radsToDegrees = rad => (rad * 180.0) / Math.PI;
radsToDegrees(Math.PI / 2); // 90

随机字母数字字符串

生成具有指定长度的随机字符串。

  • 使用 Array.from() 创建具有指定长度的新数组。
  • 使用 Math.random() 生成随机浮点数。
  • 使用基数值为 36 的 Number.prototype.toString() 将其转换为字母数字字符串。
  • 使用 String.prototype.slice() 从每个生成的数字中删除整数部分和小数点。
  • 使用 Array.prototype.some() 根据需要多次重复此过程,直到长度为止,因为它每次都会生成一个可变长度的字符串。
  • 最后,如果生成的字符串长于给定长度,则使用 String.prototype.slice() 来缩减生成的字符串。
const randomAlphaNumeric = length => {
  let s = '';
  Array.from({ length }).some(() => {
    s += Math.random().toString(36).slice(2);
    return s.length >= length;
  });
  return s.slice(0, length);
};
randomAlphaNumeric(5); // '0afad'

随机布尔值

生成一个随机布尔值。

  • 用于Math.random()生成随机数并检查它是否大于或等于0.5
const randomBoolean = () => Math.random() >= 0.5;
randomBoolean(); // true

随机十六进制颜色代码

生成一个随机的十六进制颜色代码。

  • 用于Math.random()生成一个随机的 24 位(6 * 4 位)十六进制数。
  • 使用位移位,然后使用 Number.prototype.toString() 将其转换为十六进制字符串。
const randomHexColorCode = () => {
  let n = (Math.random() * 0xfffff * 1000000).toString(16);
  return '#' + n.slice(0, 6);
};
randomHexColorCode(); // '#e34155'

范围内的随机整数数组

生成指定范围内的 n 个随机整数的数组。
  • 用于Array.from()创建特定长度的空数组。
  • 用于Math.random()生成随机数并将它们映射到所需的范围,用于Math.floor()使它们成为整数。
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 ]

范围内的随机整数

生成指定范围内的随机整数。

  • 用于Math.random()生成随机数并将其映射到所需范围。
  • 用来Math.floor()使它成为一个整数。
const randomIntegerInRange = (min, max) =>
  Math.floor(Math.random() * (max - min + 1)) + min;

randomIntegerInRange(0, 5); // 2

范围内的随机数

生成指定范围内的随机数。

  • 用于Math.random()生成随机值,使用乘法将其映射到所需范围。
const randomNumberInRange = (min, max) => Math.random() * (max - min) + min;

randomNumberInRange(2, 10); // 6.0211363285087005

范围生成器

创建一个生成器,它使用给定的步骤生成给定范围内的所有值。

  • 使用 while 循环从头到尾迭代,使用 yield 返回每个值,然后逐步递增。
  • 省略第三个参数 step 以使用默认值 1。
const rangeGenerator = function* (start, end, step = 1) {
  let i = start;
  while (i < end) {
    yield i;
    i += step;
  }
};

for (let i of rangeGenerator(6, 10)) console.log(i);
// Logs 6, 7, 8, 9

数组排名

根据比较器函数计算数组的排名。

  • 使用 Array.prototype.map() 和 Array.prototype.filter() 使用提供的比较函数将每个元素映射到一个等级。
const ranking = (arr, compFn) =>
  arr.map(a => arr.filter(b => compFn(a, b)).length + 1);

ranking([8, 6, 9, 5], (a, b) => a < b);
// [2, 3, 1, 4]
ranking(['c', 'a', 'b', 'd'], (a, b) => a.localeCompare(b) > 0);
// [3, 1, 2, 4]

读取文件行

返回指定文件中的行数组。

  • 使用 fs.readFileSync() 从文件创建Buffer
  • Buffer.prototype.toString()将缓冲区转换为字符串。
  • String.prototype.split()根据文件内容创建行数组。
const fs = require('fs');

const readFileLines = filename =>
  fs
    .readFileSync(filename)
    .toString('UTF8')
    .split('\n');
/*
contents of test.txt :
  line1
  line2
  line3
  ___________________________
*/
let arr = readFileLines('test.txt');
console.log(arr); // ['line1', 'line2', 'line3']

重新排列函数参数

创建一个调用提供的函数的函数,其参数根据指定的索引排列。

  • 使用 Array.prototype.map() 根据索引重新排序参数。
  • 使用扩展运算符 (...) 将转换后的参数传递给 fn。
const rearg = (fn, indexes) => (...args) => fn(...indexes.map(i => args[i]));
var rearged = rearg(
  function(a, b, c) {
    return [a, b, c];
  },
  [2, 0, 1]
);
rearged('b', 'c', 'a'); // ['a', 'b', 'c']

记录动画帧

在每个动画帧上调用提供的回调。

  • 使用递归。
  • 如果 running 为真,则继续调用 Window.requestAnimationFrame() 以调用提供的回调。
  • 返回具有两种方法的对象 start 和 stop 以允许手动控制记录。
  • 省略第二个参数 autoStart,以在调用函数时隐式调用 start。
const recordAnimationFrames = (callback, autoStart = true) => {
  let running = false,
    raf;
  const stop = () => {
    if (!running) return;
    running = false;
    cancelAnimationFrame(raf);
  };
  const start = () => {
    if (running) return;
    running = true;
    run();
  };
  const run = () => {
    raf = requestAnimationFrame(() => {
      callback();
      if (running) run();
    });
  };
  if (autoStart) start();
  return { start, stop };
};
const cb = () => console.log('Animation frame fired');
const recorder = recordAnimationFrames(cb);
// 在每个动画帧上记录“Animation frame fired”
recorder.stop(); // 停止记录
recorder.start(); // 重新开始
const recorder2 = recordAnimationFrames(cb, false);
// 需要显式调用 `start` 开始记录帧

重定向到网址

重定向到指定的 URL。

  • 使用Window.location.hrefWindow.location.replace()重定向到url
  • 传递第二个参数来模拟链接点击(true- 默认)或 HTTP 重定向(false)。
const redirect = (url, asLink = true) =>
  asLink ? (window.location.href = url) : window.location.replace(url);
redirect('https://google.com');

连续值数组

对累加器和数组中的每个元素(从左到右)应用一个函数,返回一个连续减少值的数组。

  • 用于Array.prototype.reduce()将给定函数应用于给定数组,存储每个新结果。
const reduceSuccessive = (arr, fn, acc) =>
  arr.reduce(
    (res, val, i, arr) => (res.push(fn(res.slice(-1)[0], val, i, arr)), res),
    [acc]
  );

reduceSuccessive([1, 2, 3, 4, 5, 6], (acc, val) => acc + val, 0);
// [0, 1, 3, 6, 10, 15, 21]

基于提供的函数的数组的最小值和最大值

在应用提供的函数设置比较规则后,返回数组的最小值/最大值。

  • Array.prototype.reduce()与函数结合使用comparator以获取数组中的适当元素。
  • 省略第二个参数 ,comparator以使用返回数组中最小元素的默认参数。
const reduceWhich = (arr, comparator = (a, b) => a - b) =>
  arr.reduce((a, b) => (comparator(a, b) >= 0 ? b : a));
reduceWhich([1, 3, 2]); // 1
reduceWhich([1, 3, 2], (a, b) => b - a); // 3
reduceWhich(
  [
    { name: 'Tom', age: 12 },
    { name: 'Jack', age: 18 },
    { name: 'Lucy', age: 9 }
  ],
  (a, b) => a.age - b.age
); // {name: 'Lucy', age: 9}

根据条件过滤对象数组,同时也过滤掉未指定的键。

  • 使用 Array.prototype.filter() 根据谓词 fn 过滤数组,以便它返回条件返回真值的对象。
  • 在过滤后的数组上,使用 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'}]

拒绝不匹配的值

根据谓词函数过滤数组的值,仅返回谓词函数返回 false 的值。

  • 将 Array.prototype.filter() 与谓词函数 pred 结合使用,以仅返回它返回 false 的值。
const reject = (pred, array) => array.filter((...args) => !pred(...args));
reject(x => x % 2 === 0, [1, 2, 3, 4, 5]); // [1, 3, 5]
reject(word => word.length > 4, ['Apple', 'Pear', 'Kiwi', 'Banana']);
// ['Pear', 'Kiwi']

从数组中删除匹配元素

通过删除给定函数返回的元素来改变数组false

  • Array.prototype.filter()查找返回真值的数组元素。
  • 使用 Array.prototype.reduce() 来移除使用 Array.prototype.splice() 的元素。
  • 使用三个参数(值、索引、数组)调用回调函数。
const remove = (arr, func) =>
  Array.isArray(arr)
    ? arr.filter(func).reduce((acc, val) => {
      arr.splice(arr.indexOf(val), 1);
      return acc.concat(val);
    }, [])
    : [];
remove([1, 2, 3, 4], n => n % 2 === 0); // [2, 4]

删除口音(音调)

从字符串中删除重音。

  • 用于String.prototype.normalize()将字符串转换为规范化的 Unicode 格式。
  • 用于String.prototype.replace()将给定 Unicode 范围内的变音符号替换为空字符串。
const removeAccents = str =>
  str.normalize('NFD').replace(/[\u0300-\u036f]/g, '');
removeAccents('Antoine de Saint-Exupéry'); // 'Antoine de Saint-Exupery'

删除元素属性

从 HTML 元素中删除所有属性。

  • 使用Element.attributesandObject.values()获取元素的所有属性。
  • 使用Array.prototype.forEach()和对象解构来获取每个属性的名称并将Element.removeAttribute()其从元素中删除。
const removeAttributes = element =>
  Object.values(element.attributes).forEach(({ name }) =>
    element.removeAttribute(name)
  );
removeAttributes(document.querySelector('p.special'));
// 该段落将不再有“特殊”类别

从 HTML 元素中删除类

从 HTML 元素中删除一个类。

  • 使用Element.classListandDOMTokenList.remove()从元素中删除指定的类。
const removeClass = (el, className) => el.classList.remove(className);
removeClass(document.querySelector('p.special'), 'special');
// The paragraph will not have the 'special' class anymore

删除 DOM 元素

从 DOM 中移除一个元素。

  • 用于Node.parentNode获取给定元素的父节点。
  • 用于Node.removeChild()从其父节点中删除给定元素。
const removeElement = el => el.parentNode.removeChild(el);
removeElement(document.querySelector('#my-element'));
// 从 DOM 中删除#my-element

从目标中移除事件侦听器

从所有提供的目标中分离事件侦听器。

  • 使用 Array.prototype.forEach() 和 EventTarget.removeEventListener() 从所有目标中分离为给定事件类型提供的侦听器。
const removeEventListenerAll = (
  targets,
  type,
  listener,
  options,
  useCapture
) => {
  targets.forEach(target =>
    target.removeEventListener(type, listener, options, useCapture)
  );
};

const linkListener = () => console.log('Clicked a link');
document.querySelector('a').addEventListener('click', linkListener);
removeEventListenerAll(document.querySelectorAll('a'), 'click', linkListener);

删除非 ASCII 字符

删除不可打印的 ASCII 字符。

  • String.prototype.replace()与正则表达式一起使用以删除不可打印的 ASCII 字符。
const removeNonASCII = str => str.replace(/[^\x20-\x7E]/g, '');
removeNonASCII('äÄçÇéÉêlorem-ipsumöÖÐþúÚ'); // 'lorem-ipsum'

删除空格

返回删除了空格的字符串。

  • 与正则表达式一起使用String.prototype.replace(),将所有出现的空白字符替换为空字符串。
const removeWhitespace = str => str.replace(/\s+/g, '');
removeWhitespace('Lorem ipsum.\n Dolor sit amet. ');
// 'Loremipsum.Dolorsitamet.'

重命名对象键

用提供的值替换多个对象键的名称。

  • 将 Object.keys() 与 Array.prototype.reduce() 和扩展运算符 (...) 结合使用,以获取对象的键并根据 keysMap 重命名它们。
const renameKeys = (keysMap, obj) =>
  Object.keys(obj).reduce(
    (acc, key) => ({
      ...acc,
      ...{ [keysMap[key] || key]: obj[key] }
    }),
    {}
  );
const obj = { name: 'Bobo', job: 'Front-End Master', shoeSize: 100 };
renameKeys({ name: 'firstName', job: 'passion' }, obj);
// { firstName: 'Bobo', passion: 'Front-End Master', shoeSize: 100 }

渲染 DOM 元素

在指定的 DOM 元素中呈现给定的 DOM 树。

  • 将第一个参数解构为type和props。 使用 type 确定给定元素是否为文本元素。
  • 根据元素的类型,使用 Document.createTextNode() 或 Document.createElement() 创建 DOM 元素。
  • 使用 Object.keys() 向 DOM 元素添加属性并根据需要设置事件侦听器。
  • 使用递归渲染 props.children,如果有的话。
  • 最后,使用 Node.appendChild() 将 DOM 元素追加到指定的容器中。
const renderElement = ({ type, props = {} }, container) => {
  const isTextElement = !type;
  const element = isTextElement
    ? document.createTextNode('')
    : document.createElement(type);

  const isListener = p => p.startsWith('on');
  const isAttribute = p => !isListener(p) && p !== 'children';

  Object.keys(props).forEach(p => {
    if (isAttribute(p)) element[p] = props[p];
    if (!isTextElement && isListener(p))
      element.addEventListener(p.toLowerCase().slice(2), props[p]);
  });

  if (!isTextElement && props.children && props.children.length)
    props.children.forEach(childElement =>
      renderElement(childElement, element)
    );

  container.appendChild(element);
};
const myElement = {
  type: 'button',
  props: {
    type: 'button',
    className: 'btn',
    onClick: () => alert('Clicked'),
    children: [{ props: { nodeValue: 'Click me' } }]
  }
};

renderElement(myElement, document.body);

重复生成器

创建一个生成器,无限期地重复给定的值。

  • 使用非终止 while 循环,每次调用 Generator.prototype.next() 时都会产生一个值。
  • 如果传递的值不是未定义的,则使用 yield 语句的返回值来更新返回值。
const repeatGenerator = function* (val) {
  let v = val;
  while (true) {
    let newV = yield v;
    if (newV !== undefined) v = newV;
  }
};
const repeater = repeatGenerator(5);
repeater.next(); // { value: 5, done: false }
repeater.next(); // { value: 5, done: false }
repeater.next(4); // { value: 4, done: false }
repeater.next(); // { value: 4, done: false }

替换字符串中的最后一次出现

替换字符串中最后一次出现的模式。

  • 使用 typeof 确定 pattern 是字符串还是正则表达式。
  • 如果模式是字符串,则将其用作匹配项。
  • 否则,使用 RegExp 构造函数创建一个新的正则表达式,使用模式的 RegExp.prototype.source 并向其添加 'g' 标志。 使用 String.prototype.match() 和 Array.prototype.slice() 获取最后一个匹配项(如果有)。
  • 使用 String.prototype.lastIndexOf() 查找字符串中匹配项的最后一次出现。
  • 如果找到匹配项,则使用 String.prototype.slice() 和模板文字将匹配的子字符串替换为给定的替换项。
  • 如果未找到匹配项,则返回原始字符串。
const replaceLast = (str, pattern, replacement) => {
  const match =
    typeof pattern === 'string'
      ? pattern
      : (str.match(new RegExp(pattern.source, 'g')) || []).slice(-1)[0];
  if (!match) return str;
  const last = str.lastIndexOf(match);
  return last !== -1
    ? `${str.slice(0, last)}${replacement}${str.slice(last + match.length)}`
    : str;
};
replaceLast('abcabdef', 'ab', 'gg'); // 'abcggdef'
replaceLast('abcabdef', /ab/, 'gg'); // 'abcggdef'
replaceLast('abcabdef', 'ad', 'gg'); // 'abcabdef'
replaceLast('abcabdef', /ad/, 'gg'); // 'abcabdef'

替换或追加数组值

替换数组中的项目或追加它(如果它不存在)。

  • 使用扩展运算符 ( ...) 创建数组的浅拷贝。
  • 使用 Array.prototype.findIndex() 查找满足提供的比较函数 compFn 的第一个元素的索引。
  • 如果未找到此类元素,请使用 Array.prototype.push() 将新值附加到数组。
  • 否则,使用Array.prototype.splice()新值替换找到的索引处的值。
const replaceOrAppend = (arr, val, compFn) => {
  const res = [...arr];
  const i = arr.findIndex(v => compFn(v, val));
  if (i === -1) res.push(val);
  else res.splice(i, 1, val);
  return res;
};
const people = [ { name: 'John', age: 30 }, { name: 'Jane', age: 28 } ];
const jane = { name: 'Jane', age: 29 };
const jack = { name: 'Jack', age: 28 };
replaceOrAppend(people, jane, (a, b) => a.name === b.name);
// [ { name: 'John', age: 30 }, { name: 'Jane', age: 29 } ]
replaceOrAppend(people, jack, (a, b) => a.name === b.name);
// [
//   { name: 'John', age: 30 },
//   { name: 'Jane', age: 28 },
//   { name: 'Jack', age: 28 }
// ]

不缓存模块

从缓存中删除模块后加载模块(如果存在)。

  • 用于delete从缓存中删除模块(如果存在)。
  • 用于require()再次加载模块。
const requireUncached = module => {
  delete require.cache[require.resolve(module)];
  return require(module);
};

const fs = requireUncached('fs'); // 'fs' 每次都会重新加载

在给定的时间后解决promise

创建一个在给定时间后解析为提供的值的Promise

  • 使用Promise构造函数创建一个新的Promise
  • 用于在setTimeout()指定resolve的.value`delay`
const resolveAfter = (value, delay) =>
  new Promise(resolve => {
    setTimeout(() => resolve(value, delay));
  });

resolveAfter('Hello', 1000);
// 返回 1 秒后解析为“Hello”的Promise

反转数字

反转数字。

  • 用于Object.prototype.toString()转换n为字符串。
  • 使用String.prototype.split(),Array.prototype.reverse()Array.prototype.join()获取n字符串的反转值。
  • 用于parseFloat()将字符串转换为数字并Math.sign()保留其符号。
const reverseNumber = n =>
  parseFloat(`${n}`.split('').reverse().join('')) * Math.sign(n);
reverseNumber(981); // 189
reverseNumber(-500); // -5
reverseNumber(73.6); // 6.37
reverseNumber(-5.23); // -32.5

反转字符串

反转字符串。

  • 使用展开运算符 ( ...) 和Array.prototype.reverse()反转字符串中字符的顺序。
  • 组合字符以使用Array.prototype.join().
const reverseString = str => [...str].reverse().join('');
reverseString('foobar'); // 'raboof'

右子串生成器

生成给定字符串的所有右子串。

  • String.prototype.length如果字符串为空,则用于提前终止。
  • 使用 for...in 循环和 String.prototype.slice() 生成给定字符串的每个子字符串,从末尾开始。
const rightSubstrGenerator = function* (str) {
  if (!str.length) return;
  for (let i in str) yield str.slice(-i - 1);
};
[...rightSubstrGenerator('hello')];
// [ 'o', 'lo', 'llo', 'ello', 'hello' ]

给定精度的整数

将数字四舍五入到指定的位数。

  • 使用Math.round()和模板文字将数字四舍五入到指定的位数。
  • 省略第二个参数 ,decimals以四舍五入为整数。
const round = (n, decimals = 0) =>
  Number(`${Math.round(`${n}e${decimals}`)}e-${decimals}`);

round(1.005, 2); // 1.01

异步运行函数

使用Web Worker在单独的线程中运行一个函数,允许长时间运行的函数不阻塞 UI。

  • 使用 Blob 对象 URL 创建一个 Worker,其内容应该是所提供函数的字符串化版本。
  • 立即发布回调函数的返回值。
  • 返回一个 Promise,监听 onmessage 和 onerror 事件并解析从 worker 传回的数据,或者抛出一个错误。
const runAsync = fn => {
  const worker = new Worker(
    URL.createObjectURL(new Blob([`postMessage((${fn})());`]), {
      type: 'application/javascript; charset=utf-8'
    })
  );
  return new Promise((res, rej) => {
    worker.onmessage = ({ data }) => {
      res(data), worker.terminate();
    };
    worker.onerror = err => {
      rej(err), worker.terminate();
    };
  });
};
const longRunningFunction = () => {
  let result = 0;
  for (let i = 0; i < 1000; i++)
    for (let j = 0; j < 700; j++)
      for (let k = 0; k < 300; k++) result = result + i + j + k;

  return result;
};
/*
  注意:由于该函数在不同的上下文中运行,因此不支持闭包。
   提供给 runAsync 的函数被字符串化,所以一切都变成了文字。
   所有变量和函数都必须在里面定义。
*/
runAsync(longRunningFunction).then(console.log); // 209685000000
runAsync(() => 10 ** 3).then(console.log); // 1000
let outsideVariable = 50;
runAsync(() => typeof outsideVariable).then(console.log); // 'undefined'

连续运行promise

连续运行一系列promise。

  • 用于Array.prototype.reduce()创建promise链,其中每个promise在解决后返回下一个promise。
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)]);
// 按顺序执行每个承诺,总共需要 3 秒才能完成

数组中的随机元素

从数组中获取一个随机元素。

  • 用于Math.random()生成随机数。
  • 将其乘以 Array.prototype.length 并使用 Math.floor() 将其四舍五入为最接近的整数。
  • 此方法也适用于字符串。
const sample = arr => arr[Math.floor(Math.random() * arr.length)];

sample([3, 7, 9, 11]); // 9

数组中的 N 个随机元素

从数组的唯一键处获取n随机元素,最大为数组的大小。

  • 使用Fisher-Yates 算法打乱数组。
  • 用于Array.prototype.slice()获取第一个n元素。
  • 省略第二个参数 ,n以仅从数组中随机获取一个元素。
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]

滚动页面到顶部

平滑滚动到页面顶部。

  • 使用 Document.documentElement 或 Document.body 和 Element.scrollTop 从顶部获取距离。
  • 从顶部滚动一小部分距离。
  • 使用 Window.requestAnimationFrame() 为滚动设置动画。
const scrollToTop = () => {
  const c = document.documentElement.scrollTop || document.body.scrollTop;
  if (c > 0) {
    window.requestAnimationFrame(scrollToTop);
    window.scrollTo(0, c - c / 8);
  }
};

将字符串转为数字

将输入字符串散列为整数。

  • 使用String.prototype.split()Array.prototype.reduce()创建输入字符串的散列,利用位移。
const sdbm = str => {
  let arr = str.split('');
  return arr.reduce(
    (hashCode, currentVal) =>
      (hashCode =
        currentVal.charCodeAt(0) +
        (hashCode << 6) +
        (hashCode << 16) -
        hashCode),
    0
  );
};

sdbm('name'); // -3521204949

选择排序算法

使用选择排序算法对数字数组进行排序。

  • 使用扩展运算符 ( ...) 克隆原始数组arr.
  • 使用for循环遍历数组中的元素。
  • 使用Array.prototype.slice()和`Array.prototype.reduce()`查找子数组中当前索引右侧的最小元素的索引。如有执行交换。
const selectionSort = arr => {
  const a = [...arr];
  for (let i = 0; i < a.length; i++) {
    const min = a
      .slice(i + 1)
      .reduce((acc, val, j) => (val < a[acc] ? j + i + 1 : acc), i);
    if (min !== i) [a[i], a[min]] = [a[min], a[i]];
  }
  return a;
};
selectionSort([5, 1, 4, 2, 3]); // [1, 2, 3, 4, 5]

序列化cookie

将 cookie 名称-值对序列化为 Set-Cookie 标头字符串。

  • 使用模板文字并encodeURIComponent()创建适当的字符串。
const serializeCookie = (name, val) =>
  `${encodeURIComponent(name)}=${encodeURIComponent(val)}`;
serializeCookie('foo', 'bar'); // 'foo=bar'

表单转为查询字符串

将一组表单元素编码为查询字符串。

  • 使用FormData构造函数将 HTML 转换formFormData.
  • 用于Array.from()转换为数组,将映射函数作为第二个参数传递。
  • 使用Array.prototype.map()andencodeURIComponent()对每个字段的值进行编码。
  • 与适当的参数一起使用Array.prototype.join()以生成适当的查询字符串。
const serializeForm = form =>
  Array.from(new FormData(form), field =>
    field.map(encodeURIComponent).join('=')
  ).join('&');
serializeForm(document.querySelector('#form'));
// email=test%40email.com&name=Test%20Name

为元素设置样式

为指定的 HTML 元素设置 CSS 规则的值。

  • 使用 HTMLElement.style 将指定元素的 CSS 规则的值设置为 val。
const setStyle = (el, rule, val) => (el.style[rule] = val);
setStyle(document.querySelector('p'), 'font-size', '20px');
// 页面上的第一个 <p> 元素的字体大小为 20px

浅克隆对象(浅拷贝对象)

创建对象的浅克隆。

  • 使用Object.assign()和一个空对象 ( {}) 创建原始对象的浅克隆。
const shallowClone = obj => Object.assign({}, obj);
const a = { x: true, y: 1 };
const b = shallowClone(a); // a !== b

从数组中删除元素

Array.prototype.splice()具有相同的功能,但返回一个新数组而不是改变原始数组。

  • 在删除现有元素和/或添加新元素后,使用 Array.prototype.slice() 和 Array.prototype.concat() 获取包含新内容的数组。
  • 省略第二个参数 index,从 开始0
  • 省略第三个参数 delCount,以删除0个元素。
  • 省略第四个参数 elements,以便不添加任何新元素。
const shank = (arr, index = 0, delCount = 0, ...elements) =>
  arr
    .slice(0, index)
    .concat(elements)
    .concat(arr.slice(index + delCount));
const names = ['alpha', 'bravo', 'charlie'];
const namesAndDelta = shank(names, 1, 0, 'delta');
// [ 'alpha', 'delta', 'bravo', 'charlie' ]
const namesNoBravo = shank(names, 1, 1); // [ 'alpha', 'charlie' ]
console.log(names); // ['alpha', 'bravo', 'charlie']

显示元素

显示指定的所有元素。

  • 使用扩展运算符 (...) 和 Array.prototype.forEach() 清除每个指定元素的display属性。
const show = (...el) => [...el].forEach(e => (e.style.display = ''));
show(...document.querySelectorAll('img'));
// 显示页面上的所有 <img> 元素

洗牌数组(数组重排)

随机化数组值的顺序,返回一个新数组。

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]

数组交集

返回出现在两个数组中的元素数组。

  • 用于Array.prototype.includes()确定不属于 的值values
  • 用于Array.prototype.filter()删除它们。
const similarity = (arr, values) => arr.filter(v => values.includes(v));

similarity([1, 2, 3], [1, 2, 4]); // [1, 2]

数组、对象或字符串的大小

获取数组、对象或字符串的大小。

  • val获取( array,object或)的类型string
  • Array.prototype.length对数组使用属性。
  • 使用lengthsize值(如果可用)或对象的键数。
  • 使用从字符串创建的对象sizeBlobval
const size = val =>
  Array.isArray(val)
    ? val.length
    : val && typeof val === 'object'
      ? val.size || val.length || Object.keys(val).length
      : typeof val === 'string'
        ? new Blob([val]).size
        : 0;
size([1, 2, 3, 4, 5]); // 5
size('size'); // 4
size({ one: 1, two: 2, three: 3 }); // 3

延迟异步函数执行(定时Sleep函数)

延迟异步函数的执行。

  • 延迟执行函数的一部分async,通过将其置于休眠状态,返回一个Promise.
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
async function sleepyWork() {
  console.log("I'm going to sleep for 1 second.");
  await sleep(1000);
  console.log('I woke up after 1 second.');
}

字符串短横线连接

将字符串转换为短横线连接。

  • 使用String.prototype.toLowerCase()andString.prototype.trim()规范化字符串。
  • String.prototype.replace()替换空格、破折号和下划线-以及删除特殊字符。
const slugify = str =>
  str
    .toLowerCase()
    .trim()
    .replace(/[^\w\s-]/g, '')
    .replace(/[\s_-]+/g, '-')
    .replace(/^-+|-+$/g, '');

slugify('Hello World!'); // 'hello-world'

平滑滚动元素进入视图

平滑地将调用它的元素滚动到浏览器窗口的可见区域。

  • 用于Element.scrollIntoView()滚动元素。
  • 用于{ behavior: 'smooth' }平滑滚动。
const smoothScroll = element =>
  document.querySelector(element).scrollIntoView({
    behavior: 'smooth'
  });
smoothScroll('#fooBar'); // 平滑地滚动到 id 为 fooBar 的元素
smoothScroll('.fooBar');// 平滑地滚动到类为 fooBar 的第一个元素

对字符串中的字符进行排序

按字母顺序对字符串中的字符进行排序。

  • 使用展开运算符 ( ...)Array.prototype.sort()String.prototype.localeCompare()对 中的字符进行排序。
  • 重组使用Array.prototype.join()
const sortCharactersInString = str =>
  [...str].sort((a, b) => a.localeCompare(b)).join('');
sortCharactersInString('cabbage'); // 'aabbceg'

排序数组中的插入索引

查找应将值插入数组以保持其排序顺序的最低索引。

  • 松散地检查数组是否按降序排序。
  • 用于Array.prototype.findIndex()查找应插入元素的适当索引。
const sortedIndex = (arr, n) => {
  const isDescending = arr[0] > arr[arr.length - 1];
  const index = arr.findIndex(el => (isDescending ? n >= el : n <= el));
  return index === -1 ? arr.length : index;
};
sortedIndex([5, 3, 2, 1], 4); // 1
sortedIndex([30, 50], 40); // 1

基于函数在排序数组中插入索引

根据提供的迭代器函数,找到应将值插入数组以保持其排序顺序的最低索引。

  • 松散地检查数组是否按降序排序。
  • 根据迭代器函数 fn,使用 Array.prototype.findIndex() 找到应插入元素的适当索引。
const sortedIndexBy = (arr, n, fn) => {
  const isDescending = fn(arr[0]) > fn(arr[arr.length - 1]);
  const val = fn(n);
  const index = arr.findIndex(el =>
    isDescending ? val >= fn(el) : val <= fn(el)
  );
  return index === -1 ? arr.length : index;
};

sortedIndexBy([{ x: 4 }, { x: 5 }], { x: 4 }, o => o.x); // 0

排序数组中的最后插入索引

查找应将值插入数组以保持其排序顺序的最高索引。

  • 松散地检查数组是否按降序排序。
  • 使用Array.prototype.reverse()和`Array.prototype.findIndex()`查找应插入元素的适当的最后一个索引。
const sortedLastIndex = (arr, n) => {
  const isDescending = arr[0] > arr[arr.length - 1];
  const index = arr
    .reverse()
    .findIndex(el => (isDescending ? n <= el : n >= el));
  return index === -1 ? 0 : arr.length - index;
};

sortedLastIndex([10, 20, 30, 30, 40], 30); // 4

基于函数的排序数组中的最后插入索引

根据提供的迭代器函数,查找应将值插入数组以维护其排序顺序的最高索引。

  • 检查数组是否按降序排序。
  • 使用 Array.prototype.map() 将迭代器函数应用于数组的所有元素。
  • 根据提供的迭代器函数,使用 Array.prototype.reverse() 和 Array.prototype.findIndex() 找到应插入元素的适当的最后一个索引。
const sortedLastIndexBy = (arr, n, fn) => {
  const isDescending = fn(arr[0]) > fn(arr[arr.length - 1]);
  const val = fn(n);
  const index = arr
    .map(fn)
    .reverse()
    .findIndex(el => (isDescending ? val <= el : val >= el));
  return index === -1 ? 0 : arr.length - index;
};

字符串按行拆封数组

将多行字符串拆分为行数组。

  • 使用String.prototype.split()和一个正则表达式来匹配换行符并创建一个数组。
const splitLines = str => str.split(/\r?\n/);
splitLines('This\nis a\nmultiline\nstring.\n');
// ['This', 'is a', 'multiline', 'string.' , '']

从可变参数转换函数

采用可变参数函数并返回一个接受参数数组的函数。

  • 使用闭包和扩展运算符 ( ...) 将参数数组映射到函数的输入。
const spreadOver = fn => argsArr => fn(...argsArr);
const arrayMax = spreadOver(Math.max);
arrayMax([1, 2, 3]); // 3

稳定排序

执行数组的稳定排序,当项的值相同时保留项的初始索引。

  • 用于Array.prototype.map()将输入数组的每个元素与其相应的索引配对。
  • 使用 Array.prototype.sort() 和比较函数对列表进行排序,如果比较的项目相等,则保留它们的初始顺序。
  • 使用 Array.prototype.map() 转换回初始数组项。
  • 不改变原始数组,而是返回一个新数组。
const stableSort = (arr, compare) =>
  arr
    .map((item, index) => ({ item, index }))
    .sort((a, b) => compare(a.item, b.item) || a.index - b.index)
    .map(({ item }) => item);
const arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const stable = stableSort(arr, () => 0); // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

标准偏差

计算数字数组的标准偏差。

  • 使用 Array.prototype.reduce() 计算值的均值、方差和方差之和,并确定标准差。
  • 省略第二个参数 usePopulation 以获得样本标准偏差或将其设置为 true 以获得总体标准偏差。
const standardDeviation = (arr, usePopulation = false) => {
  const mean = arr.reduce((acc, val) => acc + val, 0) / arr.length;
  return Math.sqrt(
    arr
      .reduce((acc, val) => acc.concat((val - mean) ** 2), [])
      .reduce((acc, val) => acc + val, 0) /
      (arr.length - (usePopulation ? 0 : 1))
  );
};
standardDeviation([10, 2, 38, 23, 38, 23, 21]); // 13.284434142114991 (sample)
standardDeviation([10, 2, 38, 23, 38, 23, 21], true);
// 12.29899614287479 (population)

字符串以子字符串开头

检查给定字符串是否以另一个字符串的子字符串开头。

  • 使用 for...in 循环和 String.prototype.slice() 获取给定单词的每个子字符串,从头开始。
  • 使用 String.prototype.startsWith() 根据文本检查当前子字符串。
  • 如果找到,则返回匹配的子字符串。 否则,返回未定义。
const startsWithSubstring = (text, word) => {
  for (let i in word) {
    const substr = word.slice(-i - 1);
    if (text.startsWith(substr)) return substr;
  }
  return undefined;
};
startsWithSubstring('/>Lorem ipsum dolor sit amet', '<br />'); // '/>'

字符串排列

生成字符串的所有排列(包含重复项)。

  • 使用递归。
  • 对于给定字符串中的每个字母,为其其余字母创建所有部分排列。
  • 使用 Array.prototype.map() 将字母与每个部分排列结合起来。
  • 使用 Array.prototype.reduce() 将所有排列组合到一个数组中。
  • 基本情况是 String.prototype.length 等于 2 或 1。
  • ⚠️ 警告:执行时间随每个字符呈指数增长。 任何超过 8 到 10 个字符都会导致您的环境在尝试解决所有不同组合时挂起。
const stringPermutations = str => {
  if (str.length <= 2) return str.length === 2 ? [str, str[1] + str[0]] : [str];
  return str
    .split('')
    .reduce(
      (acc, letter, i) =>
        acc.concat(
          stringPermutations(str.slice(0, i) + str.slice(i + 1)).map(
            val => letter + val
          )
        ),
      []
    );
};
stringPermutations('abc'); // ['abc', 'acb', 'bac', 'bca', 'cab', 'cba']

循环引用对象转JSON

将包含循环引用的 JSON 对象序列化为 JSON 格式。

  • 使用 WeakSet.prototype.add() 和 WeakSet.prototype.has() 创建一个 WeakSet 来存储和检查可见值。
  • 将 JSON.stringify() 与自定义替换函数一起使用,该函数会省略已经看到的值,并根据需要添加新值。
  • ⚠️ 注意:此函数查找并删除循环引用,这会导致序列化 JSON 中的循环数据丢失。
const stringifyCircularJSON = obj => {
  const seen = new WeakSet();
  return JSON.stringify(obj, (k, v) => {
    if (v !== null && typeof v === 'object') {
      if (seen.has(v)) return;
      seen.add(v);
    }
    return v;
  });
};

const obj = { n: 42 };
obj.obj = obj;
stringifyCircularJSON(obj); // '{"n": 42}'

剥离 HTML 标签

从字符串中删除 HTML/XML 标签。

  • 使用正则表达式从字符串中删除 HTML/XML 标记。
const stripHTMLTags = str => str.replace(/<[^>]*>/g, '');
stripHTMLTags('<p><em>lorem</em> <strong>ipsum</strong></p>'); // 'lorem ipsum'

可迭代的子集

检查第一个可迭代对象是否是第二个可迭代对象的子集,不包括重复值。

  • 使用 Set 构造函数从每个可迭代对象创建一个新的 Set 对象。
  • 使用 Array.prototype.every() 和 Set.prototype.has() 检查第一个可迭代对象中的每个值是否包含在第二个可迭代对象中。
const subSet = (a, b) => {
  const sA = new Set(a), sB = new Set(b);
  return [...sA].every(v => sB.has(v));
};

subSet(new Set([1, 2]), new Set([1, 2, 3, 4])); // true
subSet(new Set([1, 5]), new Set([1, 2, 3, 4])); // false

数组总和

计算两个或多个数字/数组的总和。

  • 用于Array.prototype.reduce()将每个值添加到累加器,用值初始化0
const sum = (...arr) => [...arr].reduce((acc, val) => acc + val, 0);

sum(1, 2, 3, 4); // 10
sum(...[1, 2, 3, 4]); // 10

映射(对象)数组总和

在使用提供的函数将每个元素映射到一个值后,计算数组的总和。

  • 用于Array.prototype.map()将每个元素映射到 返回的值fn
  • 用于Array.prototype.reduce()将每个值添加到累加器,用值初始化0
const sumBy = (arr, fn) =>
  arr
    .map(typeof fn === 'function' ? fn : val => val[fn])
    .reduce((acc, val) => acc + val, 0);
sumBy([{ n: 4 }, { n: 2 }, { n: 8 }, { n: 6 }], x => x.n); // 20
sumBy([{ n: 4 }, { n: 2 }, { n: 8 }, { n: 6 }], 'n'); // 20

到 n 的数字总和

1对和之间的所有数字求和n

  • 使用公式(n * (n + 1)) / 2计算 1 和 之间所有数字的总和n
const sumN = n => (n * (n + 1)) / 2;
sumN(100); // 5050

范围内的幂和

start计算从到end(包括两者)的所有数字的幂之和。

  • 用于Array.prototype.fill()创建目标范围内所有数字的数组。
  • 使用 Array.prototype.map() 和指数运算符 (**) 将它们提升为幂,并使用 Array.prototype.reduce() 将它们相加。
  • 省略第二个参数 power,以使用默认的幂 2。
  • 省略第三个参数 start,以使用默认起始值 1。
const sumPower = (end, power = 2, start = 1) =>
  Array(end + 1 - start)
    .fill(0)
    .map((x, i) => (i + start) ** power)
    .reduce((a, b) => a + b, 0);

sumPower(10); // 385
sumPower(10, 3); // 3025
sumPower(10, 3, 5); // 2925

可迭代的超集

检查第一个可迭代对象是否是第二个可迭代对象的超集,不包括重复值。

  • 使用Set构造函数从每个可迭代对象创建一个新Set对象。
  • 使用Array.prototype.every()和`Set.prototype.has()`检查第二个可迭代对象中的每个值是否包含在第一个可迭代对象中。
const superSet = (a, b) => {
  const sA = new Set(a), sB = new Set(b);
  return [...sB].every(v => sA.has(v));
};
superSet(new Set([1, 2, 3, 4]), new Set([1, 2])); // true
superSet(new Set([1, 2, 3, 4]), new Set([1, 5])); // false

设备支持触摸事件

检查是否支持触摸事件。

  • 检查是否'ontouchstart'存在于Window.
const supportsTouchEvents = () =>
  window && 'ontouchstart' in window;
supportsTouchEvents(); // true

交换字符串

创建一个将大写字符转换为小写字符的字符串,反之亦然。

  • 使用扩展运算符 ( ...) 转换str为字符数组。
  • 使用String.prototype.toLowerCase()和`String.prototype.toUpperCase()`将小写字符转换为大写,反之亦然。
  • 用于Array.prototype.map()将转换应用于每个字符,Array.prototype.join()以组合回字符串。
  • 请注意,这不一定是真的swapCase(swapCase(str)) === str
const swapCase = str =>
  [...str]
    .map(c => (c === c.toLowerCase() ? c.toUpperCase() : c.toLowerCase()))
    .join('');

swapCase('Hello world!'); // 'hELLO WORLD!'

Symbol化对象键

创建一个新对象,将每个键转换为Symbol.

  • 用于Object.keys()获取 的键obj
  • 使用Array.prototype.reduce()和`Symbol创建一个新对象,其中每个键都转换为Symbol`.
const symbolizeKeys = obj =>
  Object.keys(obj).reduce(
    (acc, key) => ({ ...acc, [Symbol(key)]: obj[key] }),
    {}
  );

symbolizeKeys({ id: 10, name: 'apple' });
// { [Symbol(id)]: 10, [Symbol(name)]: 'apple' }

数组差集

返回两个数组之间的对称差异,不过滤掉重复值。

  • 从每个数组创建一个Set以获取每个数组的唯一值。
  • 在它们中的每一个上使用Array.prototype.filter()以仅保留不包含在另一个中的值。
const symmetricDifference = (a, b) => {
  const sA = new Set(a),
    sB = new Set(b);
  return [...a.filter(x => !sB.has(x)), ...b.filter(x => !sA.has(x))];
};

symmetricDifference([1, 2, 3], [1, 2, 4]); // [3, 4]
symmetricDifference([1, 2, 2], [1, 3, 1]); // [2, 2, 3]

映射数组差集

将提供的函数应用于两个数组的每个数组元素后,返回两个数组之间的对称差异。

  • 从每个数组创建一个 Set 以在对它们应用 fn 后获取每个数组的唯一值。
  • 在它们中的每一个上使用 Array.prototype.filter() 只保留不包含在另一个中的值。
const symmetricDifferenceBy = (a, b, fn) => {
  const sA = new Set(a.map(v => fn(v))),
    sB = new Set(b.map(v => fn(v)));
  return [...a.filter(x => !sB.has(fn(x))), ...b.filter(x => !sA.has(fn(x)))];
};
symmetricDifferenceBy([2.1, 1.2], [2.3, 3.4], Math.floor); // [ 1.2, 3.4 ]
symmetricDifferenceBy(
  [{ id: 1 }, { id: 2 }, { id: 3 }],
  [{ id: 1 }, { id: 2 }, { id: 4 }],
  i => i.id
);
// [{ id: 3 }, { id: 4 }]

指定函数数组差集

使用提供的函数作为比较器,返回两个数组之间的对称差异。

  • 使用Array.prototype.filter()Array.prototype.findIndex()找到合适的值。
const symmetricDifferenceWith = (arr, val, comp) => [
  ...arr.filter(a => val.findIndex(b => comp(a, b)) === -1),
  ...val.filter(a => arr.findIndex(b => comp(a, b)) === -1)
];
symmetricDifferenceWith(
  [1, 1.2, 1.5, 3, 0],
  [1.9, 3, 0, 3.9],
  (a, b) => Math.round(a) === Math.round(b)
); // [1, 1.2, 3.9]

数组尾

返回数组中除第一个元素之外的所有元素。

  • 如果 Array.prototype.length 大于 1,则使用 Array.prototype.slice() 返回没有第一个元素的数组。
  • 否则,返回整个数组。
const tail = arr => (arr.length > 1 ? arr.slice(1) : arr);
tail([1, 2, 3]); // [2, 3]
tail([1]); // [1]

删除数组元素

创建一个从开头移除 n 个元素的数组。

  • 使用 Array.prototype.slice() 创建数组的一个切片,其中 n 个元素从头开始。
const take = (arr, n = 1) => arr.slice(0, n);
take([1, 2, 3], 5); // [1, 2, 3]
take([1, 2, 3], 0); // []

从末尾删除数组元素

创建一个数组,其中的n元素从末尾移除。

  • 用于Array.prototype.slice()创建数组的一部分,其中的n元素取自末尾。
const takeRight = (arr, n = 1) => arr.slice(arr.length - n, arr.length);
takeRight([1, 2, 3], 2); // [ 2, 3 ]

从末尾删除数组元素,直到满足条件

从数组末尾移除元素,直到传递的函数返回true。返回删除的元素。

  • 使用扩展运算符 (...) 和 Array.prototype.reverse() 创建数组的反向副本。
  • 遍历反向副本,在 Array.prototype.entries() 上使用 for...of 循环,直到函数的返回值为真。
  • 使用 Array.prototype.slice() 返回移除的元素。
  • 回调函数 fn 接受一个参数,即元素的值。
const takeRightUntil = (arr, fn) => {
  for (const [i, val] of [...arr].reverse().entries())
    if (fn(val)) return i === 0 ? [] : arr.slice(-i);
  return arr;
};
takeRightUntil([1, 2, 3, 4], n => n < 3); // [3, 4]

满足条件时从末尾删除数组元素

从数组末尾移除元素,直到传递的函数返回false。返回删除的元素。

  • 使用扩展运算符 (...) 和 Array.prototype.reverse() 创建数组的反向副本。
  • 遍历反向副本,在 Array.prototype.entries() 上使用 for...of 循环,直到函数的返回值为 falsy。
  • 使用 Array.prototype.slice() 返回移除的元素。
  • 回调函数 fn 接受一个参数,即元素的值。
const takeRightWhile = (arr, fn) => {
  for (const [i, val] of [...arr].reverse().entries())
    if (!fn(val)) return i === 0 ? [] : arr.slice(-i);
  return arr;
};
takeRightWhile([1, 2, 3, 4], n => n >= 3); // [3, 4]

删除数组元素直到满足条件

删除数组中的元素,直到传递的函数返回 true。 返回删除的元素。

  • 遍历数组,在 Array.prototype.entries() 上使用 for...of 循环,直到函数的返回值为真。
  • 使用 Array.prototype.slice() 返回移除的元素。
  • 回调函数 fn 接受一个参数,即元素的值。
const takeUntil = (arr, fn) => {
  for (const [i, val] of arr.entries()) if (fn(val)) return arr.slice(0, i);
  return arr;
};
takeUntil([1, 2, 3, 4], n => n >= 3); // [1, 2]

满足条件时删除数组元素

删除数组中的元素,直到传递的函数返回false。返回删除的元素。

  • 遍历数组,在 Array.prototype.entries() 上使用 for...of 循环,直到函数的返回值为假。
  • 使用 Array.prototype.slice() 返回移除的元素。
  • 回调函数fn接受一个参数,即元素的值。
const takeWhile = (arr, fn) => {
  for (const [i, val] of arr.entries()) if (!fn(val)) return arr.slice(0, i);
  return arr;
};

takeWhile([1, 2, 3, 4], n => n < 3); // [1, 2]

节流函数

wait创建一个节流函数,每毫秒最多只调用一次提供的函数

  • 使用 setTimeout() 和 clearTimeout() 来限制给定的方法 fn。
  • 使用 Function.prototype.apply() 将 this 上下文应用于函数并提供必要的arguments。
  • 使用 Date.now() 来跟踪上次调用节流函数的时间。
  • 使用变量 inThrottle 来防止 fn 的第一次执行和下一个循环之间的竞争条件。
  • 省略第二个参数 wait,将超时设置为默认值 0 毫秒。
const throttle = (fn, wait) => {
  let inThrottle, lastFn, lastTime;
  return function() {
    const context = this,
      args = arguments;
    if (!inThrottle) {
      fn.apply(context, args);
      lastTime = Date.now();
      inThrottle = true;
    } else {
      clearTimeout(lastFn);
      lastFn = setTimeout(function() {
        if (Date.now() - lastTime >= wait) {
          fn.apply(context, args);
          lastTime = Date.now();
        }
      }, Math.max(wait - (Date.now() - lastTime), 0));
    }
  };
};
window.addEventListener(
  'resize',
  throttle(function(evt) {
    console.log(window.innerWidth);
    console.log(window.innerHeight);
  }, 250)
); // 最多每 250 毫秒记录一次窗口尺寸

测量函数花费的时间

测量函数执行所需的时间。

  • 使用console.time()console.timeEnd()测量开始时间和结束时间之间的差异,以确定回调执行的时间。
const timeTaken = callback => {
  console.time('timeTaken');
  const r = callback();
  console.timeEnd('timeTaken');
  return r;
};

timeTaken(() => Math.pow(2, 10)); // 1024, (logged): timeTaken: 0.02099609375ms

迭代n次

迭代回调 n 次。

  • 使用 Function.prototype.call() 调用 fn n 次或直到它返回 false。
  • 省略最后一个参数 context ,以使用undefined(或非严格模式下的全局对象)。
const times = (n, fn, context = undefined) => {
  let i = 0;
  while (fn.call(context, i) !== false && ++i < n) {}
};
var output = '';
times(5, i => (output += i));
console.log(output); // 01234

驼峰字符串

将字符串转换为驼峰式。

  • 使用 String.prototype.match() 使用适当的正则表达式将字符串分解为单词。
  • 使用 Array.prototype.map()、Array.prototype.slice()、Array.prototype.join()、String.prototype.toLowerCase() 和 String.prototype.toUpperCase() 组合它们,每个首字母大写 。
const toCamelCase = str => {
  const s =
    str &&
    str
      .match(
        /[A-Z]{2,}(?=[A-Z][a-z]+[0-9]*|\b)|[A-Z]?[a-z]+[0-9]*|[A-Z]|[0-9]+/g
      )
      .map(x => x.slice(0, 1).toUpperCase() + x.slice(1).toLowerCase())
      .join('');
  return s.slice(0, 1).toLowerCase() + s.slice(1);
};
toCamelCase('some_database_field_name'); // 'someDatabaseFieldName'
toCamelCase('Some label that needs to be camelized');
// 'someLabelThatNeedsToBeCamelized'
toCamelCase('some-javascript-property'); // 'someJavascriptProperty'
toCamelCase('some-mixed_string with spaces_underscores-and-hyphens');
// 'someMixedStringWithSpacesUnderscoresAndHyphens'

字符串转字符数组

将字符串转换为字符数组。

  • 使用扩展运算符 ( ...) 将字符串转换为字符数组。
const toCharArray = s => [...s];
toCharArray('hello'); // ['h', 'e', 'l', 'l', 'o']

数字转货币字符串

获取一个数字并以指定的货币格式返回它。

  • 用于Intl.NumberFormat启用国家/货币敏感格式。
const toCurrency = (n, curr, LanguageFormat = undefined) =>
  Intl.NumberFormat(LanguageFormat, {
    style: 'currency',
    currency: curr,
  }).format(n);
toCurrency(123456.789, 'EUR');
// €123,456.79  | currency: Euro | currencyLangFormat: Local
toCurrency(123456.789, 'USD', 'en-us');
// $123,456.79  | currency: US Dollar | currencyLangFormat: English (United States)
toCurrency(123456.789, 'USD', 'fa');
// ۱۲۳٬۴۵۶٫۷۹ ؜$ | currency: US Dollar | currencyLangFormat: Farsi
toCurrency(322342436423.2435, 'JPY');
// ¥322,342,436,423 | currency: Japanese Yen | currencyLangFormat: Local
toCurrency(322342436423.2435, 'JPY', 'fi');
// 322 342 436 423 ¥ | currency: Japanese Yen | currencyLangFormat: Finnish

数字转小数点

将数字转换为小数点格式的字符串。

  • 用于Number.prototype.toLocaleString()将数字转换为小数点格式。
const toDecimalMark = num => num.toLocaleString('en-US');

toDecimalMark(12305030388.9087); // '12,305,030,388.909'

HSL 转数组

hsl()颜色字符串转换为值数组。

  • 用于String.prototype.match()获取包含数值的 3 个字符串的数组。
  • Array.prototype.map()结合使用将Number它们转换为数值数组。
const toHSLArray = hslStr => hslStr.match(/\d+/g).map(Number);
toHSLArray('hsl(50, 10%, 10%)'); // [50, 10, 10]

HSL转对象

将颜色字符串转换hsl()为具有每种颜色值的对象。

  • 用于String.prototype.match()获取包含数值的 3 个字符串的数组。
  • Array.prototype.map()结合使用将Number它们转换为数值数组。
  • 使用数组解构将值存储到命名变量中,并从中创建适当的对象。
const toHSLObject = hslStr => {
  const [hue, saturation, lightness] = hslStr.match(/\d+/g).map(Number);
  return { hue, saturation, lightness };
};

toHSLObject('hsl(50, 10%, 10%)'); // { hue: 50, saturation: 10, lightness: 10 }

可迭代对象转哈希

将给定的可迭代对象减少为值哈希(键控数据存储)。

  • 用于Object.values()获取可迭代对象(对象或数组)的值。
  • 用于Array.prototype.reduce()迭代值并创建一个对象,以引用值为键。
const toHash = (object, key) =>
  Object.values(object).reduce((acc, data, index) => {
    acc[!key ? index : data[key]] = data;
    return acc;
  }, {});
toHash([4, 3, 2, 1]); // { 0: 4, 1: 3, 2: 2, 3: 1 }
toHash([{ a: 'label' }], 'a'); // { label: { a: 'label' } }

带时区的 ISO 格式日期

将日期转换为扩展 ISO 格式 (ISO 8601),包括时区偏移量。

  • 使用 Date.prototype.getTimezoneOffset() 获取时区偏移量并将其反转。 将其符号存储在 diff 中。
  • 定义一个辅助函数 pad(),它使用 Math.floor() 和 Math.abs() 将任何传递的数字标准化为整数,并使用 String.prototype.padStart() 将其填充为 2 位数字。
  • 使用 pad() 和 Date 原型中的内置方法来构建具有时区偏移的 ISO 8601 字符串。
const toISOStringWithTimezone = date => {
  const tzOffset = -date.getTimezoneOffset();
  const diff = tzOffset >= 0 ? '+' : '-';
  const pad = n => `${Math.floor(Math.abs(n))}`.padStart(2, '0');
  return date.getFullYear() +
    '-' + pad(date.getMonth() + 1) +
    '-' + pad(date.getDate()) +
    'T' + pad(date.getHours()) +
    ':' + pad(date.getMinutes()) +
    ':' + pad(date.getSeconds()) +
    diff + pad(tzOffset / 60) +
    ':' + pad(tzOffset % 60);
};
toISOStringWithTimezone(new Date()); // '2020-10-06T20:43:33-04:00'

字符串string转kebab case.

将字符串转换为 kebab 大小写。

  • String.prototype.match()使用适当的正则表达式将字符串分解为单词。
  • 使用Array.prototype.map(),Array.prototype.join()String.prototype.toLowerCase()将它们组合起来,添加-为分隔符。
const toKebabCase = str =>
  str &&
  str
    .match(/[A-Z]{2,}(?=[A-Z][a-z]+[0-9]*|\b)|[A-Z]?[a-z]+[0-9]*|[A-Z]|[0-9]+/g)
    .map(x => x.toLowerCase())
    .join('-');
toKebabCase('camelCase'); // 'camel-case'
toKebabCase('some text'); // 'some-text'
toKebabCase('some-mixed_string With spaces_underscores-and-hyphens');
// 'some-mixed-string-with-spaces-underscores-and-hyphens'
toKebabCase('AllThe-small Things'); // 'all-the-small-things'
toKebabCase('IAmEditingSomeXMLAndHTML');
// 'i-am-editing-some-xml-and-html'

不带尾随零的定点表示法的数字

如果数字有小数,则使用定点表示法格式化数字。

  • 用于Number.prototype.toFixed()将数字转换为定点表示法字符串。
  • 用于Number.parseFloat()将定点表示法字符串转换为数字,删除尾随零。
  • 使用模板文字将数字转换为字符串。
const toOptionalFixed = (num, digits) =>
  `${Number.parseFloat(num.toFixed(digits))}`;
toOptionalFixed(1, 2); // '1'
toOptionalFixed(1.001, 2); // '1'
toOptionalFixed(1.500, 2); // '1.5'

序数后缀的数字

获取一个数字并将其作为具有正确序数指示器后缀的字符串返回。

  • 使用模运算符 ( %) 查找个位数和十位数的值。
  • 查找匹配的序号模式数字。
  • 如果在青少年模式中发现数字,请使用青少年序号。
const toOrdinalSuffix = num => {
  const int = parseInt(num),
    digits = [int % 10, int % 100],
    ordinals = ['st', 'nd', 'rd', 'th'],
    oPattern = [1, 2, 3, 4],
    tPattern = [11, 12, 13, 14, 15, 16, 17, 18, 19];
  return oPattern.includes(digits[0]) && !tPattern.includes(digits[1])
    ? int + ordinals[digits[0] - 1]
    : int + ordinals[3];
};
toOrdinalSuffix('123'); // '123rd'

对象转成对数组

从对象或其他可迭代对象创建键值对数组。

  • 检查是否Symbol.iterator已定义,如果已定义,则用于Array.prototype.entries()获取给定可迭代对象的迭代器。
  • 用于Array.from()将结果转换为键值对数组的数组。
  • 如果Symbol.iterator没有为 定义obj,请改用Object.entries()
const toPairs = obj =>
  obj[Symbol.iterator] instanceof Function && obj.entries instanceof Function
    ? Array.from(obj.entries())
    : Object.entries(obj);
toPairs({ a: 1, b: 2 }); // [['a', 1], ['b', 2]]
toPairs([2, 4, 8]); // [[0, 2], [1, 4], [2, 8]]
toPairs('shy'); // [['0', 's'], ['1', 'h'], ['2', 'y']]
toPairs(new Set(['a', 'b', 'c', 'a'])); // [['a', 'a'], ['b', 'b'], ['c', 'c']]

帕斯卡字符串

将字符串转换为 Pascal 大小写。

  • 使用 String.prototype.match() 使用适当的正则表达式将字符串分解为单词。
  • 使用Array.prototype.map()、Array.prototype.slice()、Array.prototype.join()、String.prototype.toUpperCase()和String.prototype.toLowerCase()组合起来,每个首字母大写 单词并将其余部分小写。
const toPascalCase = str =>
  str
    .match(/[A-Z]{2,}(?=[A-Z][a-z]+[0-9]*|\b)|[A-Z]?[a-z]+[0-9]*|[A-Z]|[0-9]+/g)
    .map(x => x.charAt(0).toUpperCase() + x.slice(1).toLowerCase())
    .join('');
toPascalCase('some_database_field_name'); // 'SomeDatabaseFieldName'
toPascalCase('Some label that needs to be pascalized');
// 'SomeLabelThatNeedsToBePascalized'
toPascalCase('some-javascript-property'); // 'SomeJavascriptProperty'
toPascalCase('some-mixed_string with spaces_underscores-and-hyphens');
// 'SomeMixedStringWithSpacesUnderscoresAndHyphens'

RGB转数组

rgb()颜色字符串转换为值数组。

  • 用于String.prototype.match()获取包含数值的 3 个字符串的数组。
  • Array.prototype.map()结合使用将Number它们转换为数值数组。
const toRGBArray = rgbStr => rgbStr.match(/\d+/g).map(Number);
toRGBArray('rgb(255, 12, 0)'); // [255, 12, 0]

RGB转对象

将 rgb() 颜色字符串转换为具有每种颜色值的对象。

  • 使用 String.prototype.match() 获取一个包含 3 个字符串和数值的数组。
  • 将 Array.prototype.map() 与 Number 结合使用,将它们转换为数值数组。
  • 使用数组解构将值存储到命名变量中,并从中创建适当的对象。
const toRGBObject = rgbStr => {
  const [red, green, blue] = rgbStr.match(/\d+/g).map(Number);
  return { red, green, blue };
};
toRGBObject('rgb(255, 12, 0)'); // {red: 255, green: 12, blue: 0}

整数转罗马数字

将整数转换为其罗马数字表示形式。1接受与3999(包括两者)之间的值。

  • 创建一个包含(罗马值,整数)形式的 2 值数组的查找表。
  • 使用 Array.prototype.reduce() 循环查找中的值,并重复将 num 除以该值。
  • 使用 String.prototype.repeat() 将罗马数字表示添加到累加器。
const toRomanNumeral = num => {
  const lookup = [
    ['M', 1000],
    ['CM', 900],
    ['D', 500],
    ['CD', 400],
    ['C', 100],
    ['XC', 90],
    ['L', 50],
    ['XL', 40],
    ['X', 10],
    ['IX', 9],
    ['V', 5],
    ['IV', 4],
    ['I', 1],
  ];
  return lookup.reduce((acc, [k, v]) => {
    acc += k.repeat(Math.floor(num / v));
    num = num % v;
    return acc;
  }, '');
};
toRomanNumeral(3); // 'III'
toRomanNumeral(11); // 'XI'
toRomanNumeral(1998); // 'MCMXCVIII'

安全整数值

将值转换为安全整数。

  • 使用Math.max()Math.min()找到最接近的安全值。
  • 用于Math.round()转换为整数。
const toSafeInteger = num =>
  Math.round(
    Math.max(Math.min(num, Number.MAX_SAFE_INTEGER), Number.MIN_SAFE_INTEGER)
  );
toSafeInteger('3.2'); // 3
toSafeInteger(Infinity); // 9007199254740991

蛇形命名法(snake_case)

将字符串转换为蛇形大小写。

  • 用于String.prototype.match()使用适当的正则表达式将字符串分解为单词。
  • 使用 Array.prototype.map()、Array.prototype.slice()、Array.prototype.join() 和 String.prototype.toLowerCase() 组合它们,添加 _ 作为分隔符。
const toSnakeCase = str =>
  str &&
  str
    .match(/[A-Z]{2,}(?=[A-Z][a-z]+[0-9]*|\b)|[A-Z]?[a-z]+[0-9]*|[A-Z]|[0-9]+/g)
    .map(x => x.toLowerCase())
    .join('_');
toSnakeCase('camelCase'); // 'camel_case'
toSnakeCase('some text'); // 'some_text'
toSnakeCase('some-mixed_string With spaces_underscores-and-hyphens');
// 'some_mixed_string_with_spaces_underscores_and_hyphens'
toSnakeCase('AllThe-small Things'); // 'all_the_small_things'
toSnakeCase('IAmEditingSomeXMLAndHTML');
// 'i_am_editing_some_xml_and_html'

标题字符串

将字符串转换为标题大小写。

  • 用于String.prototype.match()使用适当的正则表达式将字符串分解为单词。
  • 使用 Array.prototype.map()、Array.prototype.slice()、Array.prototype.join() 和 String.prototype.toUpperCase() 组合它们,将每个单词的首字母大写,并在它们之间添加一个空格。
const toTitleCase = str =>
  str
    .match(/[A-Z]{2,}(?=[A-Z][a-z]+[0-9]*|\b)|[A-Z]?[a-z]+[0-9]*|[A-Z]|[0-9]+/g)
    .map(x => x.charAt(0).toUpperCase() + x.slice(1))
    .join(' ');

toTitleCase('some_database_field_name'); // 'Some Database Field Name'
toTitleCase('Some label that needs to be title-cased');
// 'Some Label That Needs To Be Title Cased'
toTitleCase('some-package-name'); // 'Some Package Name'
toTitleCase('some-mixed_string with spaces_underscores-and-hyphens');
// 'Some Mixed String With Spaces Underscores And Hyphens'

切换 HTML 元素的类名

切换 HTML 元素的类。

  • 使用Element.classListDOMTokenList.toggle()切换元素的指定类。
const toggleClass = (el, className) => el.classList.toggle(className);

toggleClass(document.querySelector('p.special'), 'special');
// 该段落将不再有“特殊”类别

切换数组中的元素

如果元素包含在数组中,则从数组中删除该元素,如果不包含,则将其推入数组。

  • 用于Array.prototype.includes()检查给定元素是否在数组中。
  • Array.prototype.filter()如果元素在数组中,则用于删除该元素。
  • ...如果元素不在数组中,请使用扩展运算符 ( ) 将其压入。
const toggleElement = (arr, val) =>
  arr.includes(val) ? arr.filter(el => el !== val) : [...arr, val];
toggleElement([1, 2, 3], 2); // [1, 3]
toggleElement([1, 2, 3], 4); // [1, 2, 3, 4]

明天的日期

产生明天日期的字符串表示形式。

  • 使用 Date 构造函数获取当前日期。
  • 使用 Date.prototype.getDate() 将其递增 1,然后使用 Date.prototype.setDate() 将值设置为结果。
  • 使用 Date.prototype.toISOString() 返回 yyyy-mm-dd 格式的字符串。
const tomorrow = () => {
  let d = new Date();
  d.setDate(d.getDate() + 1);
  return d.toISOString().split('T')[0];
};
tomorrow(); // 2018-10-19 (if current date is 2018-10-18)

倒转对象(值为键 键为值)

对累加器和对象中的每个键应用函数(从左到右)。

  • Object.keys()迭代对象中的每个键。
  • Array.prototype.reduce()对给定的累加器应用指定的函数。
const transform = (obj, fn, acc) =>
  Object.keys(obj).reduce((a, k) => fn(a, obj[k], k, obj), acc);

transform(
  { a: 1, b: 2, c: 1 },
  (r, v, k) => {
    (r[v] || (r[v] = [])).push(k);
    return r;
  },
  {}
); // { '1': ['a', 'c'], '2': ['b'] }

转置数组

转置一个二维数组。

  • 用于Array.prototype.map()创建给定二维数组的转置。
const transpose = arr => arr[0].map((col, i) => arr.map(row => row[i]));
transpose([[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]);
// [[1, 4, 7, 10], [2, 5, 8, 11], [3, 6, 9, 12]

在 HTML 元素上触发事件

在给定元素上触发特定事件,可选择传递自定义数据。

  • 使用 CustomEvent 构造函数根据指定的eventType和details创建事件。
  • 用于EventTarget.dispatchEvent()在给定元素上触发新创建的事件。
  • detail如果您不想将自定义数据传递给触发的事件,请省略第三个参数。
const triggerEvent = (el, eventType, detail) =>
  el.dispatchEvent(new CustomEvent(eventType, { detail }));
triggerEvent(document.getElementById('myId'), 'click');
triggerEvent(document.getElementById('myId'), 'click', { username: 'bob' });

截断字符串

将字符串截断至指定长度。

  • 判断String.prototype.length是否大于num
  • 返回截断为所需长度的字符串,并附'...'加到末尾或原始字符串。
const truncateString = (str, num) =>
  str.length > num ? str.slice(0, num > 3 ? num - 3 : num) + '...' : str;

truncateString('boomerang', 7); // 'boom...'

在空格处截断字符串

将字符串截断至指定长度,尽可能保留空格。

  • 确定 String.prototype.length 是否大于或等于 lim。 如果没有,请按原样返回。
  • 使用 String.prototype.slice() 和 String.prototype.lastIndexOf() 找到所需限制下最后一个空间的索引。
  • 使用 String.prototype.slice() 根据 lastSpace 适当地截断 str,如果可能的话尊重空格并在末尾附加结尾。
  • 省略第三个参数 ending,以使用默认结尾 '...'。
const truncateStringAtWhitespace = (str, lim, ending = '...') => {
  if (str.length <= lim) return str;
  const lastSpace = str.slice(0, lim - ending.length + 1).lastIndexOf(' ');
  return str.slice(0, lastSpace > 0 ? lastSpace : lim - ending.length) + ending;
};
truncateStringAtWhitespace('short', 10); // 'short'
truncateStringAtWhitespace('not so short', 10); // 'not so...'
truncateStringAtWhitespace('trying a thing', 10); // 'trying...'
truncateStringAtWhitespace('javascripting', 10); // 'javascr...'

真伪采集

检查谓词函数对于集合的所有元素是否为真。

  • 用于Array.prototype.every()检查每个传递的对象是否具有指定的属性以及它是否返回真值。
const truthCheckCollection = (collection, pre) =>
  collection.every(obj => obj[pre]);
truthCheckCollection(
  [
    { user: 'Tinky-Winky', sex: 'male' },
    { user: 'Dipsy', sex: 'male' },
  ],
  'sex'
); // true

单形参函数

创建一个最多接受一个参数的函数,忽略任何其他参数。

  • 调用提供的函数,fn仅提供第一个参数。
const unary = fn => val => fn(val);
['6', '8', '10'].map(unary(parseInt)); // [6, 8, 10]

非柯里化函数(取消柯里化)

取消柯里化深度为 n 的函数。

  • 返回可变参数函数。
  • 在提供的参数上使用 Array.prototype.reduce() 来调用函数的每个后续 curry 级别。
  • 如果提供的参数的长度小于 n 则抛出错误。
  • 否则,使用 Array.prototype.slice() 使用适当数量的参数调用 fn。
  • 省略第二个参数,n以取消柯里化到深度1
const uncurry = (fn, n = 1) => (...args) => {
  const next = acc => args => args.reduce((x, y) => x(y), acc);
  if (n > args.length) throw new RangeError('Arguments too few!');
  return next(fn)(args.slice(0, n));
};
const add = x => y => z => x + y + z;
const uncurriedAdd = uncurry(add, 3);
uncurriedAdd(1, 2, 3); // 6

转义 HTML

Unescapes 转义的 HTML 字符。

  • 将 String.prototype.replace() 与匹配需要取消转义的字符的正则表达式一起使用。
  • 使用函数的回调,使用字典(对象)将每个转义字符实例替换为其关联的未转义字符。
const unescapeHTML = str =>
  str.replace(
    /&amp;|&lt;|&gt;|&#39;|&quot;/g,
    tag =>
      ({
        '&amp;': '&',
        '&lt;': '<',
        '&gt;': '>',
        '&#39;': "'",
        '&quot;': '"'
      }[tag] || tag)
  );

unescapeHTML('&lt;a href=&quot;#&quot;&gt;Me &amp; you&lt;/a&gt;');
// '<a href="#">Me & you</a>'

展开对象

使用键的路径展开对象。

  • 使用嵌套的 Array.prototype.reduce() 将平面路径转换为叶节点。
  • 使用 String.prototype.split() 用点分隔符拆分每个键,并使用 Array.prototype.reduce() 针对键添加对象。
  • 如果当前累加器已经包含针对特定键的值,则将其值作为下一个累加器返回。
  • 否则,将适当的键值对添加到累加器对象并将值作为累加器返回。
const unflattenObject = obj =>
  Object.keys(obj).reduce((res, k) => {
    k.split('.').reduce(
      (acc, e, i, keys) =>
        acc[e] ||
        (acc[e] = isNaN(Number(keys[i + 1]))
          ? keys.length - 1 === i
            ? obj[k]
            : {}
          : []),
      res
    );
    return res;
  }, {});
unflattenObject({ 'a.b.c': 1, d: 1 }); // { a: { b: { c: 1 } }, d: 1 }
unflattenObject({ 'a.b': 1, 'a.c': 2, d: 3 }); // { a: { b: 1, c: 2 }, d: 3 }
unflattenObject({ 'a.b.0': 8, d: 3 }); // { a: { b: [ 8 ] }, d: 3 }

展开数组

使用迭代器函数和初始种子值构建一个数组。

  • 使用 while 循环和 Array.prototype.push() 重复调用该函数,直到它返回 false。
  • 迭代器函数接受一个参数(种子),并且必须始终返回一个包含两个元素([value, nextSeed])的数组,否则返回 false 以终止。
const unfold = (fn, seed) => {
  let result = [],
    val = [null, seed];
  while ((val = fn(val[1]))) result.push(val[0]);
  return result;
};
var f = n => (n > 50 ? false : [-n, n + 10]);
unfold(f, 10); // [-10, -20, -30, -40, -50]

数组并集

返回存在于两个数组中的任何一个中的每个元素至少一次。

  • 使用 a 和 b 的所有值创建一个 Set 并将其转换为数组。
const union = (a, b) => Array.from(new Set([...a, ...b]));
union([1, 2, 3], [4, 3, 2]); // [1, 2, 3, 4]

数组根据函数并集

在将提供的函数应用于两个数组的每个数组元素之后,至少返回一次存在于两个数组中任何一个的每个元素。

  • 通过将所有 fn 应用于 a 的所有值来创建一个集合。
  • 从 a 和 b 中的所有元素创建一个集合,其值在应用 fn 后与先前创建的集合中的值不匹配。
  • 返回最后转换为数组的集合。
const unionBy = (a, b, fn) => {
  const s = new Set(a.map(fn));
  return Array.from(new Set([...a, ...b.filter(x => !s.has(fn(x)))]));
};

unionBy([2.1], [1.2, 2.3], Math.floor); // [2.1, 1.2]
unionBy([{ id: 1 }, { id: 2 }], [{ id: 2 }, { id: 3 }], x => x.id)
// [{ id: 1 }, { id: 2 }, { id: 3 }]

基于函数的数组并集

使用提供的比较器函数,返回两个数组中任何一个中存在的每个元素至少一次。

  • 使用 Array.prototype.findIndex() 创建一个集合,其中包含 a 的所有值和 b 中的值,比较器在 a 中找不到匹配项。
const unionWith = (a, b, comp) =>
  Array.from(
    new Set([...a, ...b.filter(x => a.findIndex(y => comp(x, y)) === -1)])
  );
unionWith(
  [1, 1.2, 1.5, 3, 0],
  [1.9, 3, 0, 3.9],
  (a, b) => Math.round(a) === Math.round(b)
);
// [1, 1.2, 1.5, 3, 0, 3.9]

数组中的唯一值

查找数组中的所有唯一值。

  • Set从给定数组创建一个以丢弃重复值。
  • 使用扩展运算符 ( ...) 将其转换回数组。
const uniqueElements = arr => [...new Set(arr)];
uniqueElements([1, 2, 2, 3, 4, 4, 5]); // [1, 2, 3, 4, 5]

基于函数的数组中的唯一值

根据提供的比较器函数查找数组的所有唯一值。

  • 基于比较器函数 fn,使用 Array.prototype.reduce() 和 Array.prototype.some() 创建一个仅包含每个值的第一次唯一出现的数组。
  • 比较器函数有两个参数:被比较的两个元素的值。
const uniqueElementsBy = (arr, fn) =>
  arr.reduce((acc, v) => {
    if (!acc.some(x => fn(v, x))) acc.push(v);
    return acc;
  }, []);

uniqueElementsBy(
  [
    { id: 0, value: 'a' },
    { id: 1, value: 'b' },
    { id: 2, value: 'c' },
    { id: 1, value: 'd' },
    { id: 0, value: 'e' }
  ],
  (a, b) => a.id == b.id
); // [ { id: 0, value: 'a' }, { id: 1, value: 'b' }, { id: 2, value: 'c' } ]

根据函数反转查找数组中的唯一值

根据提供的比较器函数,从右侧开始查找数组的所有唯一值。

  • 基于比较器函数 fn,使用 Array.prototype.reduceRight() 和 Array.prototype.some() 创建一个仅包含每个值的最后一次唯一出现的数组。
  • 比较器函数有两个参数:被比较的两个元素的值。
const uniqueElementsByRight = (arr, fn) =>
  arr.reduceRight((acc, v) => {
    if (!acc.some(x => fn(v, x))) acc.push(v);
    return acc;
  }, []);
uniqueElementsByRight(
  [
    { id: 0, value: 'a' },
    { id: 1, value: 'b' },
    { id: 2, value: 'c' },
    { id: 1, value: 'd' },
    { id: 0, value: 'e' }
  ],
  (a, b) => a.id == b.id
); // [ { id: 0, value: 'e' }, { id: 1, value: 'd' }, { id: 2, value: 'c' } ]

数组唯一对称差

返回两个数组之间的唯一对称差异,不包含来自任一数组的重复值。

  • 在每个数组上使用 Array.prototype.filter() 和 Array.prototype.includes() 来删除另一个数组中包含的值。
  • 从结果中创建一个集合,删除重复值。
const uniqueSymmetricDifference = (a, b) => [
  ...new Set([
    ...a.filter(v => !b.includes(v)),
    ...b.filter(v => !a.includes(v)),
  ]),
];
uniqueSymmetricDifference([1, 2, 3], [1, 2, 4]); // [3, 4]
uniqueSymmetricDifference([1, 2, 2], [1, 3, 1]); // [2, 3]

转换为绝对路径

将波形符路径转换为绝对路径。

  • 使用带正则表达式的 String.prototype.replace() 和 os.homedir() 将路径开头的 ~ 替换为主目录。
const untildify = str =>
  str.replace(/^~($|\/|\\)/, `${require('os').homedir()}$1`);
untildify('~/node'); // '/Users/aUser/node'

展开对象

从对象及其数组值属性之一生成对象数组。

  • 使用对象解构从对象中排除指定键的键值对。
  • 使用 Array.prototype.map() 作为给定键的值来创建一个对象数组。
  • 每个对象都包含原始对象的值,但映射到其各个值的键除外。
const unwind = (key, obj) => {
  const { [key]: _, ...rest } = obj;
  return obj[key].map(val => ({ ...rest, [key]: val }));
};
unwind('b', { a: true, b: [1, 2] }); // [{ a: true, b: 1 }, { a: true, b: 2 }]

取消组合数组元素

创建一个数组数组,对zip生成的数组中的元素进行解组。

  • 使用 Math.max()、Function.prototype.apply() 获取数组中最长的子数组,Array.prototype.map() 使每个元素成为一个数组。
  • 使用 Array.prototype.reduce() 和 Array.prototype.forEach() 将分组值映射到单个数组。
const unzip = arr =>
  arr.reduce(
    (acc, val) => (val.forEach((v, i) => acc[i].push(v)), acc),
    Array.from({
      length: Math.max(...arr.map(x => x.length))
    }).map(x => [])
  );

unzip([['a', 1, true], ['b', 2, false]]); // [['a', 'b'], [1, 2], [true, false]]
unzip([['a', 1, true], ['b', 2]]); // [['a', 'b'], [1, 2], [true]]

根据函数取消分组数组元素

创建一个元素数组,将zip生成的数组中的元素解组并应用提供的函数。

  • 使用 Math.max() 和扩展运算符 (...) 获取数组中最长的子数组,Array.prototype.map() 使每个元素成为一个数组。
  • 使用 Array.prototype.reduce() 和 Array.prototype.forEach() 将分组值映射到单个数组。
  • 使用 Array.prototype.map() 和扩展运算符 (...) 将 fn 应用于每个单独的元素组。
const unzipWith = (arr, fn) =>
  arr
    .reduce(
      (acc, val) => (val.forEach((v, i) => acc[i].push(v)), acc),
      Array.from({
        length: Math.max(...arr.map(x => x.length))
      }).map(x => [])
    )
    .map(val => fn(...val));
unzipWith(
  [
    [1, 10, 100],
    [2, 20, 200],
  ],
  (...args) => args.reduce((acc, v) => acc + v, 0)
);
// [3, 30, 300]

大写对象键

将对象的所有键转换为大写。

  • Object.keys()获取对象键的数组。
  • Array.prototype.reduce()将数组映射到对象,用于String.prototype.toUpperCase()将键大写。
const upperize = obj =>
  Object.keys(obj).reduce((acc, k) => {
    acc[k.toUpperCase()] = obj[k];
    return acc;
  }, {});

upperize({ Name: 'John', Age: 22 }); // { NAME: 'John', AGE: 22 }

验证号码

检查给定值是否为数字。

  • 用于parseFloat()尝试转换n为数字。
  • 使用Number.isNaN()和逻辑非 ( !) 运算符来检查num是否是一个数字。
  • 用于Number.isFinite()检查num是否是有限的。
  • 使用Number和 相等运算符 ( ==) 检查强制转换是否成立。
const validateNumber = n => {
  const num = parseFloat(n);
  return !Number.isNaN(num) && Number.isFinite(num) && Number(n) == n;
}
validateNumber('10'); // true
validateNumber('a'); // false

矢量角

计算两个向量之间的角度 (theta)。

  • 使用Array.prototype.reduce(),Math.pow()Math.sqrt()计算每个向量的大小和两个向量的标量积。
  • 使用 Math.acos() 计算反余弦并获取 theta 值。
const vectorAngle = (x, y) => {
  let mX = Math.sqrt(x.reduce((acc, n) => acc + Math.pow(n, 2), 0));
  let mY = Math.sqrt(y.reduce((acc, n) => acc + Math.pow(n, 2), 0));
  return Math.acos(x.reduce((acc, n, i) => acc + n * y[i], 0) / (mX * mY));
};
vectorAngle([3, 4], [4, 3]); // 0.283794109208328

矢量距离

计算两个向量之间的距离。

  • 使用Array.prototype.reduce(),Math.pow()Math.sqrt()计算两个向量之间的欧氏距离。
const vectorDistance = (x, y) =>
  Math.sqrt(x.reduce((acc, val, i) => acc + Math.pow(val - y[i], 2), 0));
vectorDistance([10, 0, 5], [20, 0, 10]); // 11.180339887498949

遍历对象

创建一个生成器,遍历给定对象的所有键。

  • 使用递归。
  • 定义一个生成器函数 walk,它接受一个对象和一个键数组。
  • 使用 for...of 循环和 Object.keys() 迭代对象的键。
  • 使用 typeof 检查给定对象中的每个值本身是否是一个对象。
  • 如果是这样,使用 yield* 表达式递归委托给同一个生成器函数,walk,将当前键附加到键数组。 否则,生成一个键数组,表示当前路径和给定键的值。
  • 使用 yield* 表达式委托给 walk 生成器函数。
const walkThrough = function* (obj) {
  const walk = function* (x, previous = []) {
    for (let key of Object.keys(x)) {
      if (typeof x[key] === 'object') yield* walk(x[key], [...previous, key]);
      else yield [[...previous, key], x[key]];
    }
  };
  yield* walk(obj);
};
const obj = {
  a: 10,
  b: 20,
  c: {
    d: 10,
    e: 20,
    f: [30, 40]
  },
  g: [
    {
      h: 10,
      i: 20
    },
    {
      j: 30
    },
    40
  ]
};
[...walkThrough(obj)];
/*
[
  [['a'], 10],
  [['b'], 20],
  [['c', 'd'], 10],
  [['c', 'e'], 20],
  [['c', 'f', '0'], 30],
  [['c', 'f', '1'], 40],
  [['g', '0', 'h'], 10],
  [['g', '0', 'i'], 20],
  [['g', '1', 'j'], 30],
  [['g', '2'], 40]
]
*/

一年中的一周

返回日期对应的一年中的零索引周。

  • 使用 Date 构造函数和 Date.prototype.getFullYear() 获取一年的第一天作为 Date 对象。
  • 使用 Date.prototype.setDate()、Date.prototype.getDate() 和 Date.prototype.getDay() 以及取模 (%) 运算符来获取一年中的第一个星期一。
  • 从给定日期中减去一年中的第一个星期一,然后除以一周中的毫秒数。
  • 使用 Math.round() 获取一年中与给定日期对应的零索引周。
  • 如果给定日期早于一年中的第一个星期一,则返回 -0。
const weekOfYear = date => {
  const startOfYear = new Date(date.getFullYear(), 0, 1);
  startOfYear.setDate(startOfYear.getDate() + (startOfYear.getDay() % 7));
  return Math.round((date - startOfYear) / (7 * 24 * 3600 * 1000));
};
weekOfYear(new Date('2021-06-18')); // 23

加权平均值

计算两个或多个数字的加权平均值。

  • 用于Array.prototype.reduce()创建值的加权总和和权重总和。
  • 将它们彼此相除以获得加权平均值。
const weightedAverage = (nums, weights) => {
  const [sum, weightSum] = weights.reduce(
    (acc, w, i) => {
      acc[0] = acc[0] + nums[i] * w;
      acc[1] = acc[1] + w;
      return acc;
    },
    [0, 0]
  );
  return sum / weightSum;
};

weightedAverage([1, 2, 3], [0.6, 0.2, 0.3]); // 1.72727

样本加权

从数组中获取一个随机元素,使用提供的weights作为每个元素的概率。

  • 使用 Array.prototype.reduce() 为权重中的每个值创建一个部分和数组。
  • 使用 Math.random() 生成随机数,使用 Array.prototype.findIndex() 根据之前生成的数组找到正确的索引。
  • 最后,使用生成的索引返回 arr 的元素。
const weightedSample = (arr, weights) => {
  let roll = Math.random();
  return arr[
    weights
      .reduce(
        (acc, w, i) => (i === 0 ? [w] : [...acc, acc[acc.length - 1] + w]),
        []
      )
      .findIndex((v, i, s) => roll >= (i === 0 ? 0 : s[i - 1]) && roll < v)
  ];
};

weightedSample([3, 7, 9, 11], [0.1, 0.2, 0.6, 0.1]); // 9

满足条件时应用函数

返回一个函数,该函数接受一个参数,如果为真则运行回调,如果为假则返回它。

  • 返回一个期望单个值 x 的函数,该函数根据 pred 返回适当的值。
const when = (pred, whenTrue) => x => (pred(x) ? whenTrue(x) : x);
const doubleEvenNumbers = when(x => x % 2 === 0, x => x * 2);
doubleEvenNumbers(2); // 4
doubleEvenNumbers(1); // 1

过滤掉匹配的数组元素

过滤掉数组中具有指定值之一的元素。

  • 用于Array.prototype.includes()查找要排除的值。
  • 用于Array.prototype.filter()创建排除它们的数组。
const without = (arr, ...args) => arr.filter(v => !args.includes(v));
without([2, 1, 2, 3], 1, 2); // [3]

自动换行字符串

使用字符串分隔符将字符串包装成给定数量的字符。

  • 使用 String.prototype.replace() 和正则表达式在最近的最大字符空白处插入给定的中断字符。
  • 省略第三个参数 br,使用默认值 '\n'。
const wordWrap = (str, max, br = '\n') => str.replace(
  new RegExp(`(?![^\\n]{1,${max}}$)([^\\n]{1,${max}})\\s`, 'g'), '$1' + br
);
wordWrap(
  'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce tempus.',
  32
);
// 'Lorem ipsum dolor sit amet,\nconsectetur adipiscing elit.\nFusce tempus.'
wordWrap(
  'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce tempus.',
  32,
  '\r\n'
);
// 'Lorem ipsum dolor sit amet,\r\nconsectetur adipiscing elit.\r\nFusce tempus.'

字符串转单词数组

将给定的字符串转换为单词数组。

  • 将 String.prototype.split() 与提供的模式(默认为非字母作为正则表达式)一起使用以转换为字符串数组。
  • 使用 Array.prototype.filter() 删除任何空字符串。
  • 省略第二个参数 pattern 以使用默认的正则表达式。
const words = (str, pattern = /[^a-zA-Z-]+/) =>
  str.split(pattern).filter(Boolean);
words('I love javaScript!!'); // ['I', 'love', 'javaScript']
words('python, javaScript & coffee'); // ['python', 'javaScript', 'coffee']

数组的叉积

通过从数组中创建每个可能的对,从提供的两个数组中创建一个新数组。

  • 使用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']]

逻辑异或

检查是否只有一个参数是true.

  • 对两个给定值使用逻辑或 ( ||)、与 ( &&) 和非 ( ) 运算符来创建逻辑异或。!
const xor = (a, b) => (( a || b ) && !( a && b ));

xor(true, true); // false
xor(true, false); // true
xor(false, true); // true
xor(false, false); // false

检查是/否字符串

如果字符串是 'y'/'yes' 则返回 true,如果字符串是 'n'/'no' 则返回 false。

  • 使用 RegExp.prototype.test() 检查字符串的计算结果是否为“y”/“yes”或“n”/“no”。
  • 省略第二个参数 def 以将默认答案设置为“否”。
const yesNo = (val, def = false) =>
  /^(y|yes)$/i.test(val) ? true : /^(n|no)$/i.test(val) ? false : def;
yesNo('Y'); // true
yesNo('yes'); // true
yesNo('No'); // false
yesNo('Foo', true); // true

昨天的日期

导致昨天日期的字符串表示。

  • 使用 Date 构造函数获取当前日期。
  • 使用 Date.prototype.getDate() 将其减一,然后使用 Date.prototype.setDate() 将值设置为结果。
  • 使用 Date.prototype.toISOString() 返回 yyyy-mm-dd 格式的字符串。
const yesterday = () => {
  let d = new Date();
  d.setDate(d.getDate() - 1);
  return d.toISOString().split('T')[0];
};
yesterday(); // 2018-10-17 (if current date is 2018-10-18)

分组数组元素

创建一个元素数组,根据它们在原始数组中的位置进行分组。

  • 使用 Math.max()、Function.prototype.apply() 获取参数中最长的数组。
  • 创建一个将该长度作为返回值的数组,并使用 Array.from() 和映射函数来创建分组元素的数组。
  • 如果参数数组的长度不同,则在找不到值的地方使用 undefined。
const zip = (...arrays) => {
  const maxLength = Math.max(...arrays.map(x => x.length));
  return Array.from({ length: maxLength }).map((_, i) => {
    return Array.from({ length: arrays.length }, (_, k) => arrays[k][i]);
  });
};
zip(['a', 'b'], [1, 2], [true, false]); // [['a', 1, true], ['b', 2, false]]
zip(['a'], [1, 2], [true, false]); // [['a', 1, true], [undefined, 2, false]]

将数组分组为对象

给定有效属性标识符数组和值数组,将属性与值相关联。

  • 用于Array.prototype.reduce()从两个数组构建对象。
  • 如果 的长度props大于values,则剩余的键将为undefined
  • 如果 的长度values大于props,剩余的值将被忽略。
const zipObject = (props, values) =>
  props.reduce((obj, prop, index) => ((obj[prop] = values[index]), obj), {});
zipObject(['a', 'b', 'c'], [1, 2]); // {a: 1, b: 2, c: undefined}
zipObject(['a', 'b'], [1, 2, 3]); // {a: 1, b: 2}

根据功能对数组元素进行分组

创建一个元素数组,根据原始数组中的位置进行分组,并使用函数指定应如何组合分组值。

  • 检查提供的最后一个参数是否是一个函数。
  • 使用 Math.max() 获取参数中最长的数组。
  • 使用 Array.from() 创建一个具有适当长度的数组和一个映射函数来创建分组元素的数组。
  • 如果参数数组的长度不同,则在找不到值的地方使用 undefined。
  • 使用每个组的元素调用该函数。
const zipWith = (...array) => {
  const fn =
    typeof array[array.length - 1] === 'function' ? array.pop() : undefined;
  return Array.from({ length: Math.max(...array.map(a => a.length)) }, (_, i) =>
    fn ? fn(...array.map(a => a[i])) : array.map(a => a[i])
  );
};
zipWith([1, 2], [10, 20], [100, 200], (a, b, c) => a + b + c); // [111, 222]
zipWith(
  [1, 2, 3],
  [10, 20],
  [100, 200],
  (a, b, c) =>
    (a != null ? a : 'a') + (b != null ? b : 'b') + (c != null ? c : 'c')
); // [111, 222, '3bc']

扫描二维码,在手机上阅读!
0

评论 (0)

取消