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

Bon*_*ney 3 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'
  }
];
Run Code Online (Sandbox Code Playgroud)

方法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'
  }
]
Run Code Online (Sandbox Code Playgroud)

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

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

Joh*_*ren 14

传播的代价是巨大的。我们在这里谈论的是 2 个数量级。

const { x, y } = z

z = { x, y: y + 1 } // faster
z = { ...z, y: y + 1 } // slower
Run Code Online (Sandbox Code Playgroud)

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

例如,如果你以 ES2015 为目标,Babel 实际上会发出类似于更快变体的东西,但如果你以 ES2017 为目标,你会得到更慢的变体,原样。如果您ECMASCRIPT_2018使用 Google Closure Compiler定位,则会得到较慢的变体。使用 TypeScript 编译器,您最终会得到两倍的对象,因为它执行嵌套Object.assign调用。

虽然传播速度较慢,但​​您仍然每秒获得大量操作。只是如果你以无聊的方式去做,你每秒会得到更多的操作。

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

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

如果您有一个可以传播的热代码路径,请考虑直接构建。否则,不要打扰。

  • 我喜欢在这个答案中提到“热代码路径”,而不是接受的答案在极大的对象上的扭曲。在数组中拥有一个非常大的对象而不是许多较小的对象是非常罕见的,而这些对象都需要某种默认值。为此+1! (2认同)

Nes*_*oro 10

如果有人在想知道数组扩展操作而不是对象时偶然发现这个问题:

我尝试了不同的方法来完成:

const clone = [...original]
Run Code Online (Sandbox Code Playgroud)

const clone = [...original]
Run Code Online (Sandbox Code Playgroud)
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;
}
Run Code Online (Sandbox Code Playgroud)


Anu*_*thi 6

运行第二种方法的时间会更长(即使在现代计算机上很少),因为解释器必须迭代 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();
Run Code Online (Sandbox Code Playgroud)


Cer*_*nce 5

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

但是,在现代计算机和现代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 }));
Run Code Online (Sandbox Code Playgroud)

  • 请注意将 spread 与 array.reduce() 一起使用。我怀疑它会导致 O(n^2) 行为或更糟。对于大小为 2000 的数组,以下代码在我的计算机上花费了 7 秒以上: letphoneBook=inputs.reduce((acc,entry) =&gt; { let [name,phone] =entry.trim().split(' ' ); return {...acc, [姓名]:电话}; },{}); (5认同)
  • 而使用以下代码需要 0.07 秒(100 倍的差异): let phoneBook=inputs.reduce((acc,entry) =&gt; { let [name,phone] = entry.trim().split(' '); acc[名称] = 电话;返回 acc;},{}); (3认同)
  • 请注意,我相信 `...obj, ...commonParams` 意味着在参数冲突中,通用的将获胜。而在 OP 中,他将“...commonParms”放在第一位,因此任何特定设置都会覆盖它们。 (2认同)