前言

一线大厂笔试题灵感来源

目录:

  • 第一部分:数组

  • 第二部分:函数

  • 第三部分:字符串

  • 第四部分:对象

  • 第五部分:数字

  • 第六部分:浏览器操作及其它

筛选自以下两篇文章:

  • 《127 Helpful JavaScript Snippets You Can Learn in 30 Seconds or Less》

  • 《30 seconds of code》

原本只想筛选下上面的那篇文章,在精简掉了部分多余且无用的代码片段后,感觉不够。于是顺藤摸瓜,找到了原地址:30 seconds of code

然后将所有代码段都看了遍,筛选了以下一百多段代码片段,并加入了部分自己的理解。

另外,本文工具函数的命名非常值得借鉴。

1. 第一部分:数组

1. `all`:布尔全等判断

const all = (arr, fn = Boolean) => arr.every(fn);

all([4, 2, 3], x => x > 1); // true
all([1, 2, 3]); // true

2. `allEqual`:检查数组各项相等

const allEqual = arr => arr.every(val => val === arr[0]);

allEqual([1, 2, 3, 4, 5, 6]); // false
allEqual([1, 1, 1, 1]); // true

3.`approximatelyEqual`:约等于

const approximatelyEqual = (v1, v2, epsilon = 0.001) => Math.abs(v1 - v2) < epsilon;

approximatelyEqual(Math.PI / 2.0, 1.5708); // true

4.`arrayToCSV`:数组转`CSV`格式(带空格的字符串)

const arrayToCSV = (arr, delimiter = ',') =>
  arr.map(v => v.map(x => `"${x}"`).join(delimiter)).join('\n'); arrayToCSV([['a', 'b'], ['c', 'd']]); // '"a","b"\n"c","d"'
arrayToCSV([['a', 'b'], ['c', 'd']], ';'); // '"a";"b"\n"c";"d"'

5.`arrayToHtmlList`:数组转`li`列表

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

const arrayToHtmlList = (arr, listID) =>
  (el => (
    (el = document.querySelector('#' + listID)),
    (el.innerHTML += arr.map(item => `<li>${item}</li>`).join(''))
  ))(); arrayToHtmlList(['item 1', 'item 2'], 'myListID');

6. `average`:平均数

const average = (...nums) => nums.reduce((acc, val) => acc + val, 0) / nums.length;
average(...[1, 2, 3]); // 2
average(1, 2, 3); // 2

7. `averageBy`:数组对象属性平均数

此代码段将获取数组对象属性的平均值

const averageBy = (arr, fn) =>
  arr.map(typeof fn === 'function' ? fn : val => val[fn]).reduce((acc, val) => acc + val, 0) /
  arr.length; averageBy([{ n: 4 }, { n: 2 }, { n: 8 }, { n: 6 }], o => o.n); // 5
averageBy([{ n: 4 }, { n: 2 }, { n: 8 }, { n: 6 }], 'n'); // 5

8.`bifurcate`:拆分断言后的数组

可以根据每个元素返回的值,使用reduce()push() 将元素添加到第二次参数fn中 。

const bifurcate = (arr, filter) =>
  arr.reduce((acc, val, i) => (acc[filter[i] ? 0 : 1].push(val), acc), [[], []]);
bifurcate(['beep', 'boop', 'foo', 'bar'], [true, true, false, true]); 
// [ ['beep', 'boop', 'bar'], ['foo'] ]

9. `castArray`:其它类型转数组

const castArray = val => (Array.isArray(val) ? val : [val]);

castArray('foo'); // ['foo']
castArray([1]); // [1]
castArray(1); // [1]

10. `compact`:去除数组中的无效/无用值

const compact = arr => arr.filter(Boolean);

compact([0, 1, false, 2, '', 3, 'a', 'e' * 23, NaN, 's', 34]); 
// [ 1, 2, 3, 'a', 's', 34 ]

11. `countOccurrences`:检测数值出现次数

const countOccurrences = (arr, val) => arr.reduce((a, v) => (v === val ? a + 1 : a), 0);
countOccurrences([1, 1, 2, 1, 2, 3], 1); // 3

12. `deepFlatten`:递归扁平化数组

const deepFlatten = arr => [].concat(...arr.map(v => (Array.isArray(v) ? deepFlatten(v) : v)));

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

13. `difference`:寻找差异

此代码段查找两个数组之间的差异。

const difference = (a, b) => {
  const s = new Set(b);
  return a.filter(x => !s.has(x));
}; difference([1, 2, 3], [1, 2, 4]); // [3]

14. `differenceBy`:先执行再寻找差异

在将给定函数应用于两个列表的每个元素之后,此方法返回两个数组之间的差异。

const differenceBy = (a, b, fn) => {
  const s = new Set(b.map(fn));
  return a.filter(x => !s.has(fn(x)));
}; differenceBy([2.1, 1.2], [2.3, 3.4], Math.floor); // [1.2]
differenceBy([{ x: 2 }, { x: 1 }], [{ x: 1 }], v => v.x); // [ { x: 2 } ]

15. `dropWhile`:删除不符合条件的值

此代码段从数组顶部开始删除元素,直到传递的函数返回为true

const dropWhile = (arr, func) => {
  while (arr.length > 0 && !func(arr[0])) arr = arr.slice(1);
  return arr;
}; dropWhile([1, 2, 3, 4], n => n >= 3); // [3,4]

16. `flatten`:指定深度扁平化数组

此代码段第二参数可指定深度。

const flatten = (arr, depth = 1) =>
  arr.reduce((a, v) => a.concat(depth > 1 && Array.isArray(v) ? flatten(v, depth - 1) : v), []); flatten([1, [2], 3, 4]); // [1, 2, 3, 4]
flatten([1, [2, [3, [4, 5], 6], 7], 8], 2); // [1, 2, 3, [4, 5], 6, 7, 8]

17. `indexOfAll`:返回数组中某值的所有索引

此代码段可用于获取数组中某个值的所有索引,如果此值中未包含该值,则返回一个空数组。

const indexOfAll = (arr, val) => arr.reduce((acc, el, i) => (el === val ? [...acc, i] : acc), []);

indexOfAll([1, 2, 3, 1, 2, 3], 1); // [0,3]
indexOfAll([1, 2, 3], 4); // []

18. `intersection`:两数组的交集

const intersection = (a, b) => {
  const s = new Set(b);
  return a.filter(x => s.has(x));
}; intersection([1, 2, 3], [4, 3, 2]); // [2, 3]

19. `intersectionWith`:两数组都符合条件的交集

此片段可用于在对两个数组的每个元素执行了函数之后,返回两个数组中存在的元素列表。

const intersectionBy = (a, b, fn) => {
  const s = new Set(b.map(fn));
  return a.filter(x => s.has(fn(x)));
}; intersectionBy([2.1, 1.2], [2.3, 3.4], Math.floor); // [2.1]

20. `intersectionWith`:先比较后返回交集

const intersectionWith = (a, b, comp) => a.filter(x => b.findIndex(y => comp(x, y)) !== -1);

intersectionWith([1, 1.2, 1.5, 3, 0], [1.9, 3, 0, 3.9], (a, b) => Math.round(a) === Math.round(b)); // [1.5, 3, 0]

21. `minN`:返回指定长度的升序数组

const minN = (arr, n = 1) => [...arr].sort((a, b) => a - b).slice(0, n);

minN([1, 2, 3]); // [1]
minN([1, 2, 3], 2); // [1,2]

22. `negate`:根据条件反向筛选

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

[1, 2, 3, 4, 5, 6].filter(negate(n => n % 2 === 0)); // [ 1, 3, 5 ]

23. `randomIntArrayInRange`:生成两数之间指定长度的随机数组

const randomIntArrayInRange = (min, max, n = 1) =>
  Array.from({ length: n }, () => Math.floor(Math.random() * (max - min + 1)) + min); randomIntArrayInRange(12, 35, 10); // [ 34, 14, 27, 17, 30, 27, 20, 26, 21, 14 ]

24. `sample`:在指定数组中获取随机数

const sample = arr => arr[Math.floor(Math.random() * arr.length)];

sample([3, 7, 9, 11]); // 9

25. `sampleSize`:在指定数组中获取指定长度的随机数

此代码段可用于从数组中获取指定长度的随机数,直至穷尽数组。使用Fisher-Yates算法对数组中的元素进行随机选择。

const sampleSize = ([...arr], n = 1) => {
  let m = arr.length;
  while (m) {
    const i = Math.floor(Math.random() * m--);
    [arr[m], arr[i]] = [arr[i], arr[m]];
  }
  return arr.slice(0, n);
}; sampleSize([1, 2, 3], 2); // [3,1]
sampleSize([1, 2, 3], 4); // [2,3,1]

26. `shuffle`:“洗牌” 数组

此代码段使用Fisher-Yates算法随机排序数组的元素。

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

27. `nest`:根据`parent_id`生成树结构(阿里一面真题)

根据每项的parent_id,生成具体树形结构的对象。

const nest = (items, id = null, link = 'parent_id') =>
  items
    .filter(item => item[link] === id)
    .map(item => ({ ...item, children: nest(items, item.id) }));

用法:

const comments = [
  { id: 1, parent_id: null },
  { id: 2, parent_id: 1 },
  { id: 3, parent_id: 1 },
  { id: 4, parent_id: 2 },
  { id: 5, parent_id: 4 }
];
const nestedComments = nest(comments); // [{ id: 1, parent_id: null, children: [...] }]

强烈建议去理解这个的实现,因为这是我亲身遇到的阿里一面真题:


2. 第二部分:函数

1.`attempt`:捕获函数运行异常

该代码段执行一个函数,返回结果或捕获的错误对象。

onst attempt = (fn, ...args) => {
  try {
    return fn(...args);
  } catch (e) {
    return e instanceof Error ? e : new Error(e);
  }
};
var elements = attempt(function(selector) {
  return document.querySelectorAll(selector);
}, '>_>');
if (elements instanceof Error) elements = []; // elements = []

2. `defer`:推迟执行

此代码段延迟了函数的执行,直到清除了当前调用堆栈。

const defer = (fn, ...args) => setTimeout(fn, 1, ...args);

defer(console.log, 'a'), console.log('b'); // logs 'b' then 'a'

3. `runPromisesInSeries`:运行多个`Promises`

const runPromisesInSeries = ps => ps.reduce((p, next) => p.then(next), Promise.resolve());
const delay = d => new Promise(r => setTimeout(r, d)); runPromisesInSeries([() => delay(1000), () => delay(2000)]);
//依次执行每个Promises ,总共需要3秒钟才能完成

4. `timeTaken`:计算函数执行时间

const timeTaken = callback => {
  console.time('timeTaken');
  const r = callback();
  console.timeEnd('timeTaken');
  return r;
}; timeTaken(() => Math.pow(2, 10)); // 1024, (logged): timeTaken: 0.02099609375ms

5. `createEventHub`:简单的发布/订阅模式

创建一个发布/订阅(发布-订阅)事件集线,有emitonoff方法。

  1. 使用Object.create(null)创建一个空的hub对象。

  2. emit,根据event参数解析处理程序数组,然后.forEach()通过传入数据作为参数来运行每个处理程序。

  3. on,为事件创建一个数组(若不存在则为空数组),然后.push()将处理程序添加到该数组。

  4. off,用.findIndex()在事件数组中查找处理程序的索引,并使用.splice()删除。

const createEventHub = () => ({
  hub: Object.create(null),
  emit(event, data) {
    (this.hub[event] || []).forEach(handler => handler(data));
  },
  on(event, handler) {
    if (!this.hub[event]) this.hub[event] = [];
    this.hub[event].push(handler);
  },
  off(event, handler) {
    const i = (this.hub[event] || []).findIndex(h => h === handler);
    if (i > -1) this.hub[event].splice(i, 1);
    if (this.hub[event].length === 0) delete this.hub[event];
  }
});

用法:

const handler = data => console.log(data);
const hub = createEventHub();
let increment = 0; // 订阅,监听不同事件
hub.on('message', handler);
hub.on('message', () => console.log('Message event fired'));
hub.on('increment', () => increment++); // 发布:发出事件以调用所有订阅给它们的处理程序,并将数据作为参数传递给它们
hub.emit('message', 'hello world'); // 打印 'hello world' 和 'Message event fired'
hub.emit('message', { hello: 'world' }); // 打印 对象 和 'Message event fired'
hub.emit('increment'); // increment = 1 // 停止订阅
hub.off('message', handler);

6.`memoize`:缓存函数

通过实例化一个Map对象来创建一个空的缓存。

通过检查输入值的函数输出是否已缓存,返回存储一个参数的函数,该参数将被提供给已记忆的函数;如果没有,则存储并返回它。

const memoize = fn => {
  const cache = new Map();
  const cached = function(val) {
    return cache.has(val) ? cache.get(val) : cache.set(val, fn.call(this, val)) && cache.get(val);
  };
  cached.cache = cache;
  return cached;
};

Ps: 这个版本可能不是很清晰,还有Vue源码版的:

/**
 * Create a cached version of a pure function.
 */
export function cached<F: Function> (fn: F): F {
  const cache = Object.create(null)
  return (function cachedFn (str: string) {
    const hit = cache[str]
    return hit || (cache[str] = fn(str))
  }: any)
}

7. `once`:只调用一次的函数

const once = fn => {
  let called = false
  return function () {
    if (!called) {
      called = true
      fn.apply(this, arguments)
    }
  }
};

8.`flattenObject`:以键的路径扁平化对象

使用递归。

  1. 利用Object.keys(obj)联合Array.prototype.reduce(),以每片叶子节点转换为扁平的路径节点。

  2. 如果键的值是一个对象,则函数使用调用适当的自身prefix以创建路径Object.assign()

  3. 否则,它将适当的前缀键值对添加到累加器对象。

  4. prefix除非您希望每个键都有一个前缀,否则应始终省略第二个参数。

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

9. `unflattenObject`:以键的路径展开对象

与上面的相反,展开对象。

const unflattenObject = obj =>
  Object.keys(obj).reduce((acc, k) => {
    if (k.indexOf('.') !== -1) {
      const keys = k.split('.');
      Object.assign(
        acc,
        JSON.parse(
          '{' +
            keys.map((v, i) => (i !== keys.length - 1 ? `"${v}":{` : `"${v}":`)).join('') +
            obj[k] +
            '}'.repeat(keys.length)
        )
      );
    } else acc[k] = obj[k];
    return acc;
  }, {}); unflattenObject({ 'a.b.c': 1, d: 1 }); // { a: { b: { c: 1 } }, d: 1 }

这个的用途,在做Tree组件或复杂表单时取值非常舒服。

3. 第三部分:字符串

1.`byteSize`:返回字符串的字节长度

const byteSize = str => new Blob([str]).size;

byteSize('												

【JS】403- JavaScript 工具函数大全(新)的更多相关文章

  1. 【javascript】javascript常用函数大全

    javascript函数一共可分为五类:   •常规函数   •数组函数   •日期函数   •数学函数   •字符串函数   1.常规函数   javascript常规函数包括以下9个函数:   ( ...

  2. javascript工具函数

    第一部分 JavaScript工具函数 转义特殊字符为html实体   HtmlEncode: function(str){ return str.replace(/&/g, '&') ...

  3. javascript + jquery函数大全

    JAVASCRIPT Array 函数   array创建数组 concat()连接两个或更多的数组,并返回结果. join()把数组中所有元素组成字符串. pop()删除并返回数组的最后一个元素 s ...

  4. JavaScript验证函数大全

    1. 长度限制 <script> function test() { if(document.a.b.value.length>50) { alert("不能超过50个字符 ...

  5. JS开发常用工具函数 总结

    js原生工具库 1.isStatic:检测数据是不是除了symbol外的原始数据 */ function isStatic(value) { return( typeof value === 'str ...

  6. [转]JavaScript字符串函数大全

    JS自带函数concat将两个或多个字符的文本组合起来,返回一个新的字符串.var a = "hello";var b = ",world";var c = a ...

  7. js常用的工具函数

    JS选取DOM元素的方法注意:原生JS选取DOM元素比使用jQuery类库选取要快很多1.通过ID选取元素document.getElementById('myid');2.通过CLASS选取元素do ...

  8. JavaScript字符串函数大全

    JS自带函数concat将两个或多个字符的文本组合起来,返回一个新的字符串.var a = "hello";var b = ",world";var c = a ...

  9. youku的js脚本的工具函数和初始化方法

    定义日志输出函数 (function(){ if(window['console']){ return; } window['console'] = { log: function(){} ,clea ...

随机推荐

  1. 基于@Scheduled注解的Spring定时任务

    1.创建spring-task.xml 在xml文件中加入命名空间 <beans xmlns="http://www.springframework.org/schema/beans& ...

  2. 关于css中的字体样式

    1.决定字体的属性 color:字体颜色  属性值:单词,十六进制表示,rgb 2.字体大小 font-size:12px:属性值是整数字,不要带小数,单位是px叫做像素单位:凡是由像素拼成的图片我们 ...

  3. 分组取topN

    假设有这样一个文件,文件内容如下 class1 class2 class1 class1 class2 class2 class1 class2 class1 class2 要求按照班级分组取出每个班 ...

  4. 领扣(LeetCode)有效的括号 个人题解

    给定一个只包括 '(',')','{','}','[',']' 的字符串,判断字符串是否有效. 有效字符串需满足: 左括号必须用相同类型的右括号闭合. 左括号必须以正确的顺序闭合. 注意空字符串可被认 ...

  5. linux配置安装源

    ubutu:图形界面或者/etc/apt/sources.list redhat7:可以把DVD安装盘里的软件包拷贝到硬盘,然后设置一个本地源,具体如下: /etc/yum.repos.d/local ...

  6. Entity Framework Core For MySql查询中使用DateTime.Now的问题

    背景 最近一直忙于手上澳洲线上项目的整体迁移和升级的准备工作,导致博客和公众号停更.本周终于艰难的完成了任务,借此机会,总结一下项目中遇到的一些问题. EF Core一直是我们团队中中小型项目常用的O ...

  7. JAVA数组翻转

    首先可 public class RevcArr { public static void main(String[] args) { // TODO Auto-generated method st ...

  8. springboot+logback日志输出企业实践(下)

    目录 1.引言 2. 输出 logback 状态数据 3. logback 异步输出日志 3.1 异步输出配置 3.2 异步输出原理 4. springboot 多环境下 logback 配置 5. ...

  9. 以传参的方式执行shell(模板)

    以传参的方式执行shell(模板) #!bin/bash # USE: Template # author : xiaowei # -- # state : -name 选项必选,,, -v -m 选 ...

  10. 面试官:JVM锁优化都优化了啥?

    从JDK1.6开始,JVM对锁进行了各种优化,目的就是为了在线程间更高效的共享数据和解决互斥同步的问题.从锁优化的话题开始,可以引申出很多考点面试题,比如锁优化的技术.各优化技术的细节.CAS实现原理 ...