扩频运算符会影响性能吗?

时间:2019-04-25 06:30:15

标签: javascript arrays node.js

下面,我有2种方法来构造对象数组。

方法1:

const employees = [
  {
    company: 'ABC',
    country: 'IN',
    zip: 123,
    employeeId: 123,
    employeeName: 'p'
  },
  {
    company: 'ABC',
    country: 'IN',
    zip: 123,
    employeeId: 456,
    employeeName: 'q'
  },
  {
    company: 'ABC',
    country: 'IN',
    zip: 123,
    employeeId: 789,
    employeeName: 'r'
  }
];

方法2(使用点差运算符):

const commonParams = {
  company: 'ABC',
  country: 'IN',
  zip: 123
};

const employees = [
  {
    ...commonParams,
    employeeId: 123,
    employeeName: 'p'
  },
  {
    ...commonParams,
    employeeId: 456,
    employeeName: 'q'
  },
  {
    ...commonParams,
    employeeId: 789,
    employeeName: 'r'
  }
]

方法2的代码行较少,并且向数组元素添加新的公共属性会容易得多。

但是,如果有一个较大的commonParams对象,与方法1相比,方法2(使用扩展运算符)是否会影响性能? 传播算子会为commonParams数组中的每个对象遍历employees对象中的每个属性吗?

4 个答案:

答案 0 :(得分:4)

传播的成本很高。我们在这里说两个数量级。

const { x, y } = z

z = { x, y: y + 1 } // faster
z = { ...z, y: y + 1 } // slower

虽然它们都完成相似的事情,但它们的性能特征却大不相同。但这取决于是否以及如何编译您的JavaScript。

例如,如果您定位到ES2015,Babel实际上会发出与更快的变体类似的东西,但是如果您定位到ES2017,您将得到较慢的变体,原样。如果您使用Google Closure编译器定位ECMASCRIPT_2018,则会得到较慢的版本。使用TypeScript编译器,最终得到的对象要多一倍,因为它确实嵌套了Object.assign调用。

虽然传播速度较慢,但​​您每秒仍能获得大量的运营机会。只是如果您以无聊的方式进行操作,您每秒将获得更多的操作。

我整理了一个jsperf示例来说明这一点。

https://jsperf.com/the-cost-of-spreading/1

如果您的热代码路径确实在传播,请考虑直接构建。否则,请不要打扰。

答案 1 :(得分:3)

是的,将引用一个对象的变量传播到另一个对象中需要解释器查找该变量所指的内容,然后查找该对象的所有可枚举的自身属性(以及关联的值),以使其传播插入新对象。确实确实需要一点处理能力。

但是,在现代计算机和现代JS引擎上,所需的处理能力几乎为零;每秒可以处理数百万条指令有什么关系?少数几个键值对无后顾之忧。

除非您确定要散布具有个键值对的对象,并且实际上会导致性能瓶颈,否则效果会更好避免过早优化的想法,而是旨在编写简洁易读的代码(通常可以使用扩展语法来调用)。对于大型employees数组,第二种方法比第一种方法更具可读性。

(尽管,您也可以考虑使用.map来使代码保持DRY-er:)

const employeesInitial = [
  {
    employeeId: 123,
    employeeName: 'p'
  },
  {
    employeeId: 456,
    employeeName: 'q'
  },
  {
    employeeId: 789,
    employeeName: 'r'
  }
];
const employees = employeesInitial.map((obj) => ({ ...obj, ...commonParams }));

答案 2 :(得分:1)

运行第二种方法的时间会更长(即使在现代计算机上很少),因为解释器必须遍历commonParams的键并将它们复制到每个对象。

编写基准以发现对于小物体几乎为零的差异。

function runFirstApproach(){
  const employees1 = [
    {
      company: 'ABC',
      country: 'IN',
      zip: 123,
      employeeId: 123,
      employeeName: 'p'
    },
    {
      company: 'ABC',
      country: 'IN',
      zip: 123,
      employeeId: 456,
      employeeName: 'q'
    },
    {
      company: 'ABC',
      country: 'IN',
      zip: 123,
      employeeId: 789,
      employeeName: 'r'
    }
  ];
}

function runSecondApproach() {
  const commonParams = {
    company: 'ABC',
    country: 'IN',
    zip: 123
  };

  const employees2 = [
    {
      ...commonParams,
      employeeId: 123,
      employeeName: 'p'
    },
    {
      ...commonParams,
      employeeId: 456,
      employeeName: 'q'
    },
    {
      ...commonParams,
      employeeId: 789,
      employeeName: 'r'
    }
  ]
}

function runBenchmarkWithFirstApproach(){
  console.log("Avg time to run first approach -> ", getAvgRunTime(runFirstApproach, 100000))
}

function runBenchmarkWithSecondApproach(){
  console.log("Avg time to run second approach ->", getAvgRunTime(runSecondApproach, 100000))
}

function getAvgRunTime(func, rep){
  let totalTime = 0;
  let tempRep = rep;
  while(tempRep--) {
    const startTime = Date.now();
    func();
    const endTime = Date.now();
    const timeTaken = endTime-startTime;
    totalTime += timeTaken;
  }
  return totalTime/rep;
}

runBenchmarkWithFirstApproach();
runBenchmarkWithSecondApproach();

答案 3 :(得分:1)

如果有人绊倒了这个问题,同时又想知道数组扩展操作而不是对象:

我采用了不同的方法来完成:

const clone = [...original]

在999999个数组条目上,传播操作的性能优于我能以 67ms 得出的其他方法。

因此,如果您对数组使用扩展操作,请继续使用它们!

如果您不开始这样做的话!