我们应该在TypeScript中使用_.foreach()或更好的for循环原生

Syn*_*oon 6 performance foreach typescript lodash

我刚刚开始使用TypeScript处理一个新项目.我来自另一个也使用TypeScript的项目.由于TypeScript中的循环原生可用,我们决定(旧项目团队)使用这个.对我来说,相信我的java背景,编写for循环更方便.

现在,在新项目中,他们使用_.foreach()循环来迭代数组.

我想知道的是,和_.foreach()的原生打字稿之间是否有性能差异?

我在jsperf中创建了一个小测试,他们缝合的速度或多或少完全相同......

https://jsperf.com/foreach-vs-forof/12

TypeScript For

for (let num: string of list){
  console.log(num);
}
Run Code Online (Sandbox Code Playgroud)

在JavaScript中

var list = "9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9".split();

//Transpiled TypeScript for of     | **19,937  ±5.04%
for (var _i = 0, list_1 = list; _i < list_1.length; _i++) {   
  var num = list_1[_i];   
  console.log("" + num); 
}

//lodash                           | 20,520  ±1.22%  
_.forEach(list, function(item) {
  console.log("" + item)
});
Run Code Online (Sandbox Code Playgroud)

Imho我更喜欢TypeScript的"原生",因为它对我来说更具可读性.

你们建议使用什么?还有其他要点用于或更好的_.forEach

Joh*_*ohn 6

除了我的阅读之外,我对 Typescript 没有任何经验,但我对 ES6/ES2015 确实有相当多的经验。for of过去和现在仍然是最终确定的 ES2015 规范的一部分。我会从 MDN 上阅读这篇文章for of

以下是for of和 的一些相似点和不同点forEach(这些只是我目前所发现和了解的):

  • forEachlodash 适用于数组、对象或字符串集合。

  • nativeforEach适用于数组、映射和集合。

  • for of适用于所有Iterables:数组、字符串、TypedArray、映射、集合、DOM 集合和生成器。

我会阅读Exploring ES6for of中的这一章(Exploring ES6 是一本很棒的读物。它非常彻底。它也是在线免费的。)其中的一些内容对我来说很突出,因为它与.for offorEach

在 for-of 循​​环中中断并继续工作

break并且continue没有暴露在forEach. continue在 forEach 中你可以得到的最接近的东西是 using return,它实际上几乎是相同的东西。至于虽然break我看不到其他选择(但不要低估 lodash,因为大多数需要中断的事情,比如查找和返回单个项目,已经在 lodash 的大部分库中涵盖了)。

还应该注意的是,awaitasync/await 中的关键字可以在 forEach 内部使用for of,因为 forEach 使得阻止周围的块等待 forEach 块内等待的 Promises 变得相当困难,但是可以使用,forEach尽管使用maporreduce可能会使waiting 比 forEach 简单得多(取决于您对这些函数的熟悉程度)。for of下面是使用 、 、forEach和 和reduce依次和并行等待 Promise 的三个独立实现,map只是为了看看可能的差异。

const timeout = ms => new Promise(res => setTimeout(() => res(ms), ms));

const times = [100, 50, 10, 30];

async function forOf() {
  console.log("running sequential forOf:");
  for (const time of times) {
    await timeout(time);
    console.log(`waited ${time}ms`);
  }
  console.log("running parallel forOf:");
  const promises = [];
  for (const time of times) {
    const promise = timeout(time).then(function(ms) {
      console.log(`waited ${ms}ms`);
    });
    promises.push(promise);
  }
  await Promise.all(promises);
};


async function forEach() {
  console.log("running sequential forEach:");
  let promise = Promise.resolve();
  times.forEach(function(time) {
    promise = promise.then(async function() {
      await timeout(time);
      console.log(`waited ${time}ms`);
    });
  });
  await promise;
  console.log("running parallel forEach:");
  const promises = [];
  times.forEach(function(time) {
    const promise = timeout(time).then(function(ms) {
      console.log(`waited ${ms}ms`);
    });
    promises.push(promise);
  });
  await Promise.all(promises);
};

async function reduceAndMap() {
  console.log("running sequential reduce:");
  const promise = times.reduce(function(promise, time) {
    return promise.then(async function() {
      await timeout(time);
      console.log(`waited ${time}ms`);
    });
  }, Promise.resolve());
  await promise;
  console.log("running parallel map:");
  const promises = times.map(async function(time) {
    const ms = await timeout(time)
    console.log(`waited ${ms}ms`);
  });
  await Promise.all(promises);
}

forOf().then(async function() {
  await forEach();
  await reduceAndMap();
}).then(function() {
  console.log("done");
});
Run Code Online (Sandbox Code Playgroud)

在 ES2017中,Object.entries您甚至可以轻松准确地迭代对象自己的可枚举属性和值。如果您现在想使用它,您可以使用此处的其中一个填充工具。下面是一个示例。

var obj = {foo: "bar", baz: "qux"};
    
for (let x of Object.entries(obj)) { // OK
    console.log(x); // logs ["foo", "bar"] then ["baz", "qux"]
}
Run Code Online (Sandbox Code Playgroud)

我编写的快速填充的实现。您通常也会使用数组解构,它将键和值分离到它自己的变量中,如下所示:

var obj = {foo: "bar", baz: "qux"};

for (let [key, val] of Object.entries(obj)) { // OK
    console.log(key + " " + val); // logs "foo bar" then "baz qux"
}
Run Code Online (Sandbox Code Playgroud)

您还可以Object.entriesforEach这样使用:

var obj = {foo: "bar", baz: "qux"};

console.log("without array destructuring");
Object.entries(obj).forEach((x) => { // OK
    const key = x[0], val = x[1];
    console.log(key + " " + val); // logs "foo bar" then "baz qux"
});


console.log("with array destructuring");
Object.entries(obj).forEach(([key, val]) => { // OK
    console.log(key + " " + val); // logs "foo bar" then "baz qux"
});
Run Code Online (Sandbox Code Playgroud)

forEach的第一个参数默认为您将从or循环let中获得的功能类型,这是一件好事。我的意思是,如果变量内部发生任何异步操作,则该迭代的范围将仅限于该循环的特定部分。forEach 的这个属性实际上与 let 无关,而是与 JavaScript 中函数的作用域和闭包有关,而替代方案是因为它们不是块作用域。例如,看看使用 var 时会发生什么:forfor of

const arr = [1,2,3,4,5,6,7,8,9];

for(var item of arr) {
  setTimeout(() => {
    console.log(item);
  }, 100);
}
Run Code Online (Sandbox Code Playgroud)

let与使用when 或 foreach 相反。

const arr = [1,2,3,4,5,6,7,8,9];
const timeout = 100;

console.log('for of');
for(let item of arr) {
  setTimeout(() => {
    console.log(item);
  }, timeout);
}

setTimeout(() => {
  console.log('foreach');
  arr.forEach((item) => {
    setTimeout(() => {
      console.log(item);
    }, timeout);
  })
}, timeout*arr.length);
Run Code Online (Sandbox Code Playgroud)

var再次,我将注意 using和 using letor之间的区别foreach。不同之处在于 var 的变量被提升到函数作用域的顶部(如果不在函数中则为文件),然后为整个作用域重新分配值,因此循环到达其末尾并item最后一次分配,然后每个settimeout函数都会记录最后一个item. 而 withletforeach变量item不会被覆盖,因为item其作用域为块(当使用 let 时)或函数(当使用 foreach 时)。

forEach和之间for of,您只需要决定哪一个最适合当前工作(例如,您需要breaks 或需要使用 Maps、Sets 或 Generators use for of)。除此之外,我觉得对于它们都以其核心功能进行操作的集合来说,没有特别有力的理由。此外,在处理可以使用其中之一forEach或的集合时for of,这主要取决于个人喜好,因为它们以大约相同的速度执行相同的操作(并且速度可能根据解释器随时改变)。我觉得 lodash 的独特优势在于它的其他各种功能,这些功能实际上可以节省您大量的时间,让您无需自己编写代码,例如 map、reduce、filter 和 find。因为你觉得写作最舒服,所以for of我建议你继续这样写,但是一旦你开始使用 lodash 的其他函数编写,你可能会开始觉得用 lodash 的方式写更舒服。

编辑:

查看您的代码,我注意到您的列表创建有一个错误。最后你刚刚拥有了,.split()而且你应该拥有了.split(",")。您正在创建整个字符串的长度为 1 的列表,并在该字符串上迭代一次,这就是基准标记如此相似的原因。我重新进行了测试。他们来了。我仍然不会担心每次运行时性能都会发生很大的变化。