首页前端开发JavaScriptJavaScript Reduce使用详解

JavaScript Reduce使用详解

时间2024-02-01 02:06:03发布访客分类JavaScript浏览474
导读:收集整理的这篇文章主要介绍了JavaScript Reduce使用详解,觉得挺不错的,现在分享给大家,也给大家做个参考。 目录mapfiltersomeeveryfindIndexpip...
收集整理的这篇文章主要介绍了JavaScript Reduce使用详解,觉得挺不错的,现在分享给大家,也给大家做个参考。
目录
  • map
  • filter
  • some
  • every
  • findIndex
  • pipe
    • 参考答案
      • 一、返回函数接受一个参数
      • 二、返回函数接受不定参数
  • 实现lodash.get
    • 参考答案
    • 实现lodash.flattenDeep
      • 过滤掉对象中的空值
        • enumify
          • Promise串行执行器
            • 拓展
              @H_406_56@

              学会这一个技巧 reduce 让你开启编程新世界

              Learning This Reduce Skill and a Whole New World Will OPEn up for You 🎉

              reduce 可谓是 JS 数组方法最灵活的一个,因为可以替代数组的其他方法,比如 map / filter / some / every 等,也是最难理解的一个方法,lodash 很多方法也可以用其实现,学会 reduce 将给与开发者另一种函数式(Functional)、声明式(Declarative)的视角解决问题,而不是以往的过程式(PRocedual)或命令式(Imperative)

              其中一个难点在于判断 accaccumulation 的类型以及如何选择初始值,其实有个小技巧,可以帮助我们找到合适的初始值,我们想要的返回值的类型和 acc 类型需要是一样的,比如求和最终结果是数字,则 acc 应该是数字类型,故其初始化必定是 0

              下面开始巩固对 reduce 的理解和用法。

              map

              根据小技巧,map 最终返回值是数组,故 acc 也应该是一个数组,初始值使用空数组即可。

              /** * Use `reduce` to implement the builtin `Array.prototype.map` method. * @param {
              any[]}
               arr  * @param {
                  (val: any, index: number, thisArray: any[]) =>
               any}
               mapping  * @returns {
              any[]}
               */function map(arr, mapping) {
                   return arr.reduce((acc, ITem, index) =>
                   [...acc, mapping(item, index, arr)], []);
              }
                  

              测试

              map([null, false, 1, 0, '', () =>
               {
              }
                  , NaN], val =>
                   !!val);
                  // [false, false, true, false, false, true, false]

              filter

              根据小技巧,filter 最终返回值也是数组,故 acc 也应该是一个数组,使用空数组即可。

              /** * Use `reduce` to implement the builtin `Array.prototype.filter` method. * @param {
              any[]}
               arr  * @param {
                  (val: any, index: number, thisArray: any[]) =>
               boolean}
               predicate  * @returns {
              any[]}
               */function filter(arr, predicate) {
                   return arr.reduce((acc, item, index) =>
                   predicate(item, index, arr) ? [...acc, item] : acc, []);
              }
                  

              测试

              filter([null, false, 1, 0, '', () =>
               {
              }
                  , NaN], val =>
                   !!val);
                  // [1, () =>
               {
              }
                  ]

              some

              some 当目标数组为空返回 false,故初始值为 false

              function some(arr, predicate) {
                   return arr.reduce((acc, val, idx) =>
               acc || predicate(val, idx, arr), false)}
                  

              测试:

              some([null, false, 1, 0, '', () =>
               {
              }
                  , NaN], val =>
                   !!val);
                  // truesome([null, false, 0, '', NaN], val =>
                   !!val);
                  // false

              附带提醒,二者对结果没影响但有性能区别,acc 放到前面因为是短路算法,可避免无谓的计算,故性能更高。

              acc || predicate(val, idx, arr)

              predicate(val, idx, arr) || acc

              every

              every 目标数组为空则返回 true,故初始值为 true

              function every(arr, predicate) {
                   return arr.reduce((acc, val, idx) =>
                   acc &
                  &
               predicate(val, idx, arr), true)}
                  

              findIndex

              findIndex 目标数组为空返回 -1,故初始值 -1。

              function findIndex(arr, predicate) {
                   const NOT_FOUND_INDEX = -1;
                   return arr.reduce((acc, val, idx) =>
               {
               if (acc === NOT_FOUND_INDEX) {
                   return predicate(val, idx, arr) ? idx : NOT_FOUND_INDEX;
               }
                    return acc;
               }
              , NOT_FOUND_INDEX)}
                  

              测试

              findIndex([5, 12, 8, 130, 44], (element) =>
                   element >
                   8) // 3

              pipe

              一、实现以下函数

              /** * Return a function to make the input value processed by the provided functions in sequence From left the right. * @param {
                  (funcs: any[]) =>
               any}
               funcs  * @returns {
                  (arg: any) =>
               any}
               */function pipe(...funcs) {
              }
                  

              使得

              pipe(val =>
                   val * 2, Math.sqrt, val =>
                   val + 10)(2) // 12

              利用该函数可以实现一些比较复杂的处理过程

              // 挑选出 val 是正数的项对其 val 乘以 0.1 系数,然后将所有项的 val 相加,最终得到 3const process = pipe( arr =>
               arr.filter(({
               val }
                  ) =>
                   val >
                   0),  arr =>
                   arr.map(item =>
               ({
               ...item, val: item.val * 0.1 }
                  )),  arr =>
               arr.reduce((acc, {
               val }
                  ) =>
                   acc + val, 0));
              process([{
               val: -10 }
              , {
               val: 20 }
              , {
               val: -0.1 }
              , {
               val: 10 }
                  ]) // 3

              二、实现以下函数,既能实现上述 pipe 的功能,而且返回函数接纳参数个数可不定

              /** * Return a function to make the input values processed by the provided functions in sequence from left the right. * @param {
                  (funcs: any[]) =>
               any}
               funcs  * @returns {
                  (args: any[]) =>
               any}
               */function pipe(...funcs) {
              }
                  

              使得以下单测通过

              pipe(sum, Math.sqrt, val =>
                   val + 10)(0.1, 0.2, 0.7, 3) // 12

              其中 sum 已实现

              /** * Sum up the numbers. * @param args number[] * @returns {
              number}
               the total sum. */function sum(...args) {
                   return args.reduce((a, b) =>
                   a + b);
              }
                  

              参考答案

              一、返回函数接受一个参数

              省略过滤掉非函数的 func 步骤

              /** * Return a function to make the input value processed by the provided functions in sequence from left the right. * @param {
                  (arg: any) =>
               any}
               funcs * @returns {
                  (arg: any) =>
               any}
               */function pipe(...funcs) {
                   return (arg) =>
               {
                   return funcs.reduce( (acc, func) =>
               func(acc), arg ) }
              }
                  

              二、返回函数接受不定参数

              同样省略了过滤掉非函数的 func 步骤

              /** * Return a function to make the input value processed by the provided functions in sequence from left the right. * @param {
                  Array(...args: any) =>
                   any>
              }
               funcs * @returns {
                  (...args: any[]) =>
               any}
               */function pipe(...funcs) {
                  	// const realFuncs = funcs.filter(isFunction);
                   return (...args) =>
               {
                   return funcs.reduce( (acc, func, idx) =>
               idx === 0 ? func(...acc) : func(acc), args ) }
              }
                  

              性能更好的写法,避免无谓的对比,浪费 CPU

              function pipe(...funcs) {
                   return (...args) =>
               {
                   // 第一个已经处理,只需处理剩余的 return funcs.slice(1).reduce( (acc, func) =>
               func(acc),  // 首先将特殊情况处理掉当做 `acc` funcs[0](...args) ) }
              }
                  

              第二种写法的 funcs[0](...args) 这个坑要注意,数组为空就爆炸了,因为空指针了。

              实现 lodash.get

              实现 get 使得以下示例返回 'hello world'

              const obj = {
               a: {
               b: {
               c: 'hello world' }
               }
               }
                  ;
                  get(obj, 'a.b.c');
                  

              函数签名:

              /** * pluck the value by key path * @param any object * @param keyPath string 点分隔的 key 路径 * @returns {
              any}
               目标值 */function get(obj, keyPath) {
              }
                  

              参考答案

              /** * Pluck the value by key path. * @param any object * @param keyPath string 点分隔的 key 路径 * @returns {
              any}
               目标值 */function get(obj, keyPath) {
               if (!obj) {
                   return undefined;
               }
                   return keyPath.split('.').reduce((acc, key) =>
                   acc[key], obj);
              }
                  

              实现 lodash.flattenDeep

              虽然使用 concat 和扩展运算符只能够 flatten 一层,但通过递归可以去做到深度 flatten。

              方法一:扩展运算符

              function flatDeep(arr) {
                   return arr.reduce((acc, item) =>
                Array.isArray(item) ? [...acc, ...flatDeep(item)] : [...acc, item], [] )}
                  

              方法二:concat

              function flatDeep(arr) {
                   return arr.reduce((acc, item) =>
                acc.concat(Array.isArray(item) ? flatDeep(item) : item), [] )}
                  

              有趣的性能对比,扩展操作符 7 万次 1098ms,同样的时间 concat 只能执行 2 万次

              function flatDeep(arr) {
                   return arr.reduce((acc, item) =>
                Array.isArray(item) ? [...acc, ...flatDeep(item)] : [...acc, item], [] )}
                  VAR arr = repeat([1, [2], [[3]], [[[4]]]], 20);
                  console.LOG(arr);
                  console.log(flatDeep(arr));
                  console.time('concat')for (i = 0;
                   i  7 * 10000;
               ++i) {
               flatDeep(arr)}
              console.timeEnd('concat')function repeat(arr, times) {
                   let result = [];
                   for (i = 0;
                   i  times;
               ++i) {
               result.push(...arr) }
                   return result;
               }
                  

              过滤掉对象中的空值

              实现

              clean({
               foo: null, bar: undefined, baz: 'hello' }
              )// {
               baz: 'hello' }
                  

              答案

              /** * Filter out the `nil` (null or undefined) values. * @param {
              object}
               obj * @returns {
              any}
               * * @example clean({
               foo: null, bar: undefined, baz: 'hello' }
                  ) * * // =>
               {
               baz: 'hello' }
               */export function clean(obj) {
               if (!obj) {
                   return obj;
               }
                   return Object.keys(obj).reduce((acc, key) =>
               {
               if (!isNil(obj[key])) {
                   acc[key] = obj[key];
               }
                   return acc;
               }
              , {
              }
                  );
              }
                  

              enumify

              将常量对象模拟成 TS 的枚举

              实现 enumify 使得

              const Direction = {
               UP: 0, DOWN: 1, LEFT: 2, RIGHT: 3,}
                  ;
                  const actual = enumify(Direction);
              const expected = {
               UP: 0, DOWN: 1, LEFT: 2, RIGHT: 3, 0: 'UP', 1: 'DOWN', 2: 'LEFT', 3: 'RIGHT',}
                  ;
                  deepStrictEqual(actual, expected);
                  

              答案:

              /** * Generate enum from object. * @see https://www.typescriptlang.org/play?#code/KYOwrgtgBAglDeAoKubOwAmUC8UCMANMmpgEw5SlEC+UiiAxgPYgDOTANsAHQdMDmAChjd0GAjqBuRi3ZdeA4QG08AXSmIgA * @param {
              object}
               obj * @returns {
              object}
               */export function enumify(obj) {
               if (!isPlainObject(obj)) {
                   throw new TypeError('the enumify target must be a plain object');
               }
                   return Object.keys(obj).reduce((acc, key) =>
               {
                   acc[key] = obj[key];
                   acc[obj[key]] = key;
                   return acc;
               }
              , {
              }
                  );
              }
                  

              Promise 串行执行器

              利用 reduce 我们可以让不定数量的 promises 串行执行,在实际项目中能发挥很大作用。此处不细讲,请参考我的下一篇文章 JS 请求调度器。

              拓展

              请使用 jest 作为测试框架,给本文的所有方法书写单测
              更多习题见 github.COM/you-dont-ne…

              以上就是JavaScript Reduce使用详解的详细内容,更多关于JavaScript Reduce使用的资料请关注其它相关文章!

              您可能感兴趣的文章:
              • JavaScript中reduce()的5个基本用法示例
              • JavaScript数组reduce()方法 
              • 8个JS的reduce使用实例和reduce操作方式
              • JS中的reduce()方法使用小结
              • js利用reduce方法让你的代码更加优雅
              • JavaScript reduce的基本用法详解

              声明:本文内容由网友自发贡献,本站不承担相应法律责任。对本内容有异议或投诉,请联系2913721942#qq.com核实处理,我们将尽快回复您,谢谢合作!

              上一篇: 原生js实现2048小游戏下一篇:JS 实现请求调度器猜你在找的JavaScript相关文章 html font标签如何设置字体大小?html font标签属性用法介绍2022-05-16vue3+TypeScript+vue-router的使用方法2022-04-16vue3获取当前路由地址2022-04-16如何利用React实现图片识别App2022-04-16JavaScript展开运算符和剩余运算符的区别详解2022-04-16微信小程序中使用vant框架的具体步骤2022-04-16Vue elementUI表单嵌套表格并对每行进行校验详解2022-04-16如何利用Typescript封装本地存储2022-04-16微信小程序中wxs文件的一些妙用分享2022-04-16JavaScript的Set数据结构详解2022-04-16 其他相关热搜词更多phpjavapython程序员loadpost-format-gallery

              若转载请注明出处: JavaScript Reduce使用详解
              本文地址: https://pptw.com/jishu/594778.html
              Elasticsearch工具cerebro的安装与使用教程 C语言的特点与创建的基本步骤是什么

              游客 回复需填写必要信息