【问题标题】:Procedural vs. Functional Javascript程序与函数式 Javascript
【发布时间】:2017-10-13 04:40:22
【问题描述】:

我在一个反应​​应用程序中有一段相当时间关键的代码,它会在表单中的所有用户击键时运行。它一般没有性能问题,但我正在考虑对其进行一些优化,对for (let k in obj)Object.keys(obj).reduce 之间的performance differences 有点惊讶。

我认为在 JS 中设置函数调用堆栈等会很昂贵,但以下例程的函数式版本将程序化的程序从水中吹走(整整一个数量级!)。

以下是不同的版本:

程序

const generateProps = (fields, source, start) => {
  if (!fields) return start
  let finish = {...start}
  for (let k of Object.keys(fields)) {
    const fld = fields[k]
    if (fld instanceof Array) {
      if (fld.length === 0) continue
      // Handle an array of scalars (e.g. phoneNumbers)
      if (fld[0].hasOwnProperty('value')) {
        let sf = {}
        for (let i = 0; i < fld.length; i++) {
          sf[i] = fld[i]
        }
        finish = generateProps(sf, source[k], finish)
      // Handle an array of fields (e.g. addresses)
      } else {
        for (let i = 0; i < fld.length; i++) {
          finish = generateProps(fld[i], source[k][i], finish)
        }
      }
    } else {
      finish = {
        hasError: fields[k].hasError || fields[k].value === '' || finish.hasError,
        isEditing: fields[k].editing || finish.isEditing,
        unchanged: (!fields[k].isNew && fields[k].value === source[k]) && finish.unchanged,
        hasNew: fields[k].isNew || finish.hasNew
      }
    }
  }
  return finish
}

功能性

const generateProps = (fields, source, start) => {
  if (!fields) return start
  const keys = Object.keys(fields)
  return keys.reduce((props, k) => {
    const fld = fields[k]
    if (fld instanceof Array) {
      if (fld.length === 0) return props
      // Handle an array of scalars (e.g. phoneNumbers)
      if (fld[0].hasOwnProperty('value')) return generateProps(fld.reduce((sf, f, i) => {sf[i] = f; return sf}, {}), source[k], props)
      // Handle an array of fields (e.g. addresses)
      return fld.reduce((subp, f, i) => generateProps(f, source[k][i], subp), props)
    }
    return {
      hasError: fields[k].hasError || fields[k].value === '' || props.hasError,
      isEditing: fields[k].editing || props.isEditing,
      unchanged: (!fields[k].isNew && fields[k].value === source[k]) && props.unchanged,
      hasNew: fields[k].isNew || props.hasNew
    }
  }, start)
}

这里是jperf results

正如您在运行测试时所看到的,程序版本比功能版本慢了近 50%。我很想知道为什么会有如此明显的差异。

【问题讨论】:

  • 通常情况下,您链接的that benchmark 是完全有缺陷的。
  • 除了存在缺陷之外,for(let ... in ...) 在 Firefox 中的运行速度是最后两个的两倍 .... 和 Object.keys(obj).reduce 在 Chrome 中的运行速度是前两个的两倍 - 所以在一个浏览器上进行基准测试意味着反正什么都没有:p
  • 你用的是什么浏览器? {...start} 是“过程”jsperf 中的语法错误。
  • Ubuntu 上的 Chrome 版本 63.0.3230.0
  • 我认为您的算法(或数据格式)也有问题。为什么要从 fld 数组复制到 sf 对象?在标量描述符和字段对象的情况下,您都在尝试迭代 fld - 它(至少在您的示例数据中)是 always 一个长度为 @ 的数组987654334@。您不应该在source 中迭代相应的数组吗?

标签: javascript functional-programming performance-testing procedural-programming


【解决方案1】:

好的,我发现了一个关键的区别!功能版本正在变异start(我曾假设reduce 会创建一个来自ramdajs 背景的副本),而程序正在创建一个副本。如果我将顶部 reduce 调用的最后一个参数更改为 {...start},它们差不多。

这让我想知道...为什么对象传播这个很慢?

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-09-10
    • 1970-01-01
    • 1970-01-01
    • 2010-12-06
    • 1970-01-01
    相关资源
    最近更新 更多