简体   繁体   English

传播运算符会影响性能吗?

[英]Does spread operator affect performance?

I am considering the below two approaches for building an array of objects:我正在考虑以下两种构建对象数组的方法:

Approach 1 (list all properties, even if duplicated among objects):方法 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'
  }
];

Approach 2 (avoid duplication with the spread operator ):方法 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'
  }
]

Approach 2 is more succint, and adding a new property that is common to all array elements would be much easier (and less prone to errors).方法 2 更简洁,添加一个所有数组元素共有的新属性会容易得多(并且不太容易出错)。

However, in case of a large commonParams object, does approach 2 (using the spread operator ) affect performance as compared to approach 1?但是,如果是大型commonParams对象,与方法 1 相比,方法 2(使用扩展运算符)是否会影响性能?

Would the spread operator loop through each of the properties of the commonParams object for each of the objects in the employees array?扩展运算符是否会为employees数组中的每个对象循环遍历commonParams对象的每个属性?

Yes, spreading a variable which refers to an object into another object requires the interpreter to look up what the variable refers to, and then look up all the enumerable own properties (and the associated values) of the object that gets spreaded so as to insert into the new object.是的,将引用一个对象的变量传播到另一个对象中需要解释器查找变量引用的内容,然后查找被传播的对象的所有可枚举自身属性(和关联值)以便插入进入新对象。 This does indeed take a bit of processing power.这确实需要一些处理能力。

But, on modern computers, and on modern JS engines, the processing power required is next to nothing;但是,在现代计算机和现代 JS 引擎上,所需的处理能力几乎为零; what does it matter, when millions of instructions can be processed each second?当每秒可以处理数百万条指令时,这有什么关系? A handful of key-value pairs is nothing to worry about.少数键值对无需担心。

Unless you've identified that you're spreading an object with tons of key-value pairs, and it's actually causing a performance bottleneck, it would be a better idea to avoid premature optimization and aim to write clean, readable code instead (which may well invoke using spread syntax often).除非你已经确定你正在传播一个包含大量键值对的对象,并且它实际上会导致性能瓶颈,否则最好避免过早优化并改为编写干净、可读的代码(这可能经常使用扩展语法调用)。 For a large employees array, the second approach is more readable than the first.对于大型employees数组,第二种方法比第一种方法更具可读性。

(though, you also might consider using .map , to keep the code even DRY-er:) (不过,您也可以考虑使用.map来保持代码更干爽:)

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

The cost of spreading is significant.传播的成本是巨大的。 We're talking 2 orders of magnitude here.我们在这里谈论 2 个数量级。

const { x, y } = z

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

While they both accomplish similar things they are very different in their performance characteristics.虽然它们都完成了类似的事情,但它们的性能特征却大不相同。 But it will depend, if and how your JavaScript is transpiled.但这将取决于您的 JavaScript 是否被转译以及如何转译。

For example, Babel will actually emit something which is similar to the faster variant if you target ES2015 but if you target ES2017 you'll get the slower variant, as-is.例如,如果你以 ES2015 为目标,Babel 实际上会发出类似于更快变体的东西,但如果你以 ES2017 为目标,你将按原样得到较慢的变体。 If you target ECMASCRIPT_2018 with the Google Closure Compiler you get the slower variant.如果您使用 Google Closure 编译器以ECMASCRIPT_2018为目标,您会得到较慢的变体。 With the TypeScript compiler you end up with twice as many objects because it does nested Object.assign calls.使用 TypeScript 编译器,你最终会得到两倍多的对象,因为它进行嵌套Object.assign调用。

While spreading is slower you're still getting a lot of ops per second.虽然传播速度较慢,但您每秒仍会获得很多操作。 It's just that if you do it the boring way you'll get a lot more ops per second.只是如果你以无聊的方式去做,你每秒会得到更多的操作。

I put together a jsperf example to illustrate this.我整理了一个 jsperf 示例来说明这一点。

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

If you have a hot code path that does spreading, consider direct construction.如果你有一个热代码路径会传播,请考虑直接构建。 Otherwise, don't bother.否则,不要打扰。

Time to run second approach will be longer (even if very little on modern computers) as interpreter has to iterate over keys of commonParams and copy them to each object.运行第二种方法的时间会更长(即使在现代计算机上很少),因为解释器必须遍历 commonParams 的键并将它们复制到每个对象。

Wrote a benchmark to find difference which is almost zero for small objects.写了一个基准来发现小物体几乎为零的差异。

 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();

In case someone is stumbling uppon this question while wondering about array spread operations instead of objects:如果有人在想知道数组传播操作而不是对象时绊倒了这个问题:

I benched different methods to accomplish:我采用不同的方法来完成:

const clone = [...original]

 var original = []; var clone = []; for (var i = 0; i < 10000000; i++) { original.push(1); } var cycle = 0; var spreadTime = []; var mapTime = []; var forTime = []; var reduceTime = []; var sliceTime = []; var arrayFromTime = []; while (cycle < 10) { var d = Date.now(); clone = []; clone = [...original]; spreadTime.push(Date.now() - d); d = Date.now(); clone = []; clone = original.map((entry) => entry); mapTime.push(Date.now() - d); d = Date.now(); clone = []; for (var i = 0; i < original.length; i++) { clone[i] = original[i]; } forTime.push(Date.now() - d); d = Date.now(); clone = []; clone = original.reduce((next, e) => { next.push(e); return next; }, []); reduceTime.push(Date.now() - d); d = Date.now(); clone = []; clone = original.slice(); sliceTime.push(Date.now() - d); d = Date.now(); clone = []; clone = Array.from(original); arrayFromTime.push(Date.now() - d); cycle ++; document.getElementById("cycle").innerHTML = cycle; document.getElementById("spreadTime").innerHTML = spreadTime.reduce((a,b) => a + b, 0) / spreadTime.length; document.getElementById("mapTime").innerHTML = mapTime.reduce((a,b) => a + b, 0) / mapTime.length; document.getElementById("forTime").innerHTML = forTime.reduce((a,b) => a + b, 0) / forTime.length; document.getElementById("reduceTime").innerHTML = reduceTime.reduce((a,b) => a + b, 0) / reduceTime.length; document.getElementById("sliceTime").innerHTML = sliceTime.reduce((a,b) => a + b, 0) / sliceTime.length; document.getElementById("arrayFromTime").innerHTML = arrayFromTime.reduce((a,b) => a + b, 0) / arrayFromTime.length; }
 <View> <h1>cycle <span id="cycle"></span></h1> spread: <span id="spreadTime"></span> ms <br/> map: <span id="mapTime"></span> ms <br/> for: <span id="forTime"></span> ms <br/> reduce: <span id="reduceTime"></span> ms <br/> slice: <span id="sliceTime"></span> ms <br/> arrayFrom: <span id="arrayFromTime"></span> ms <br/> </View>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM