发布于 1970-01-01 08:00
  • 1 个回答
    • 先改写一下,让你看得清楚些。

      const str = 'ddd'
      const initialVal = {}
      
      let res = str.split('').reduce((pre, cur) => (pre[cur]++ || (pre[cur] = 1), pre), initialVal)

      语法上的confusion

      可以繁写为:

      (pre, cur) => {
        return pre[cur]++ || (pre[cur] = 1), pre
      }

      形如 return 1, 3, 4 总是返回最后一个合法的值。

      这里用这种偏门写法主要是为了能在一行中写完,使其既执行语句,还能返回值pre,供给下一次遍历消费。

      算是片面追求短小。

      加了注释的ES5写法

      var str = 'ddd';
      // cache 是 reduce 函数进行累加操作时候的初始值。
      // 在这里,它主要用来缓存字符串中出现的字符和它们的出现次数。
      // 遍历结束后,它的结构可能是 { d: 3 } 。
      var cache = {};
      
      var res = str.split('').reduce(function (pre, cur) {
        // pre 是一个 plain Object, 初始值为 cache ,可能形如 { d: 1 }。
        // 而 cur 是当前遍历到的字符,可能为 d。
        var cachedTimes = pre[cur]; // 所以这一步是在向buffer(缓存)中询问,是否已经缓存过 d,并且得到它的次数。
      
        if (cachedTimes) { 
          pre[cur] += 1; // 如果有值,说明已经缓存过,加上这次访问,其次数应该 + 1
        } else {
          pre[cur] = 1; // 如果为undefined,说明没有缓存过,在缓存中注册一下,次数为1
        }
        return pre; // 把修改后的 pre 传给下一次遍历消费。
      }, cache);

      在js中,使用cache表来缓存遍历次数是一个常见的优化办法。因为js中访问属性是很快的。

      你改写的函数的错误之处

      额,其实讲到这里你应该很明白了。然而你的reduce的回调函数体中并没有返回累计结果,传给下一次遍历。所以第二次遍历时,preundefined
      你的str中第二个字符估计是dundefined.d当然报错啦。


      PS 这种写法会更优雅吗?

      回复追问

      Q1

      使用array.reduce的话必须使用返回值对吧?

      是的。必须给一个返回值,供下次遍历消费。

      Q2

      return pre[cur]++ || (pre[cur] = 1), pre 应该是return (pre[cur]++ || (pre[cur] = 1), pre)吧?

      可加可不加,你的源码中加了是因为 arrow function 直接返回一个对象时需要用括号包裹。

      为了严谨,我们用babel转译一下。

      (pre, cur) => {
        return pre[cur]++ || (pre[cur] = 1), pre
      }

      babel的转译码

      "use strict";
      
      (function (pre, cur) {
        return pre[cur]++ || (pre[cur] = 1), pre;
      });

      babel的结果是不加的。

      试试执行下面的同类代码

      function test() {
        return console.log(123), 345;
      }
      var val = test(); // 先打印123, val 为 345

      Q3

      pre[cur]++ || (pre[cur] = 1),这个式子难道不是说两个表达式只要有一个为真就返回那个真的值吗?我以为这个返回就是return的意思,所以当时就一直不懂这个式子怎么会有两个返回值(||运算其中一个结果和pre,这就是我当时的想法。)

      你分成两步看。

      return pre[cur]++ || (pre[cur] = 1), pre
      // 相当于
      var a = pre[cur]++ || (pre[cur] = 1)
      var b = pre
      return a, b

      根据return a, b 总是输出 b 的原则,输出b(即pre)。

      说实话这种偷懒的语法我也是第一次看到,额,反正业务代码里我是不会这么写的,可阅读性太差了。毕竟是example,当然很fancy了。

      2022-11-26 23:18 回答
    撰写答案
    今天,你开发时遇到什么问题呢?
    立即提问
    PHP1.CN | 中国最专业的PHP中文社区 | PNG素材下载 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
    Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有