我们应该在TypeScript中使用_.foreach()还是更好的for循环本机for [英] Should we use _.foreach() or better the native for of loop in TypeScript

查看:48
本文介绍了我们应该在TypeScript中使用_.foreach()还是更好的for循环本机for的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我刚刚开始在一个使用TypeScript的新项目中工作.我来自另一个也与TypeScript一起工作的项目.由于TypeScript中的本机for循环可用,因此我们决定(旧项目团队)使用此代码.对我而言,编写与我的Java背景相关的for循环要方便得多.

现在,在新项目中,它们在_.foreach()循环中到处都使用数组来遍历数组.

我想知道的是,of的原始打字稿与_.foreach()之间的性能差异

我已经在jsperf中创建了一个小测试,它们的接缝速度大致相同...

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

TypeScript的

for (let num: string of list){
  console.log(num);
}

在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)
});

恕我直言,我更喜欢TypeScript的本机",因为它对我来说更具可读性.

你们建议使用什么?是否还有其他要用于_.forEach

的点?

解决方案

除了阅读之外,我没有打字稿方面的经验,但是我确实对ES6/ES2015有很多经验. for of曾经是,现在仍然是已最终确定的ES2015规范的一部分.我会阅读来自MDN的for of文章.

以下是for offorEach的一些相似之处和不同之处(据我所知,目前为止):

    lodash中的
  • forEach适用于数组,对象或对象的集合 字符串.

  • 本机forEach适用于数组,地图和集合.

  • for of适用于所有 Iterables :数组,字符串,TypedArrays, 地图,集合,DOM集合和生成器.

我会从探索ES6 (<阅读.它非常彻底.它也是免费的在线.)其中一些与for of有所不同的东西对我来说很明显,而forEach中却没有.

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

breakcontinueforEach中没有公开.您可以在forEach中最接近continue的地方使用return,实际上这几乎是同一件事.至于break,虽然我没有其他选择(但不要打折lodash,因为lodash的很多库中已经涵盖了大多数需要休息的东西,例如查找和返回单个项目).

还应注意,async/await中的await关键字在for of内可用,因为forEach使得阻止周围的块等待forEach块中已等待的Proposes相当困难.可以使用forEach,尽管使用mapreduce可能比forEach更加简单(取决于您对这些功能的熟悉程度).下面是使用for offorEachreducemap依次和并行地等待诺言的三个单独的实现,只是为了查看可能的差异.

 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");
}); 

使用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"]
} 

此处是我编写的带有快速polyfill的实现.通常,您也将使用数组解构,这会将键和值分隔为自己的变量,如下所示:

 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"
} 

您也可以将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"
}); 

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

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

for(var item of arr) {
  setTimeout(() => {
    console.log(item);
  }, 100);
} 

与使用let或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); 

同样,我会指出使用var与使用letforeach之间的区别.区别在于将var的变量提升到函数作用域的顶部(如果不在函数中,则文件被悬挂),然后为整个作用域重新分配值,这样循环就结束了并为最后一次分配item,然后最后每个item的每个settimeout功能日志.而对于letforeach,变量item不会被覆盖,因为item的作用域是块(使用let时)或函数(使用foreach时).

forEachfor of之间,您只需要确定哪个最适合当前工作(例如,您是否需要break或需要使用Maps,Sets或Generators使用for of).除此之外,我觉得这两个集合都没有核心功能的理由并没有特别强烈的理由.同样,在处理可以使用forEachfor of的集合时,这主要取决于个人喜好,因为它们以大约相同的速度执行相同的操作(根据解释程序,速度可以随时更改).我觉得lodash的特殊优势在于它的其他各种功能,这些功能实际上可以节省您自己编写代码(例如map,reduce,filter和find)的大量时间.既然您最舒适地编写for of,我建议您继续采用这种方式编写,但是一旦您开始使用lodash的其他功能进行编写,您可能会开始感到更加舒适.

查看您的代码,我发现您的列表创建出错.最后,您只有.split(),而您本应该拥有.split(",").您正在创建整个字符串的长度为1的列表,并对该字符串进行一次迭代,这就是基准标记如此相似的原因.我重新测试. 在这里.我仍然不用担心每次运行都会改变多少性能.

I just started to work in a new project working with TypeScript. I'm comming from another project that also worked with TypeScript. Since the native for of loop in TypeScript is avaible we decided (old project team) to use this one. Espacialy for me it was much more convenient to write the for of loop, relating to my java background.

Now in the new project they use everywhere the _.foreach() loop to iterate over arrays.

What I am wondering, is there a performance difference between the native typescript for of and the _.foreach()

i've created a little test in jsperf they seam to be more or less exactly same speed...

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

TypeScript For of

for (let num: string of list){
  console.log(num);
}

In 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)
});

Imho i would prefer the "native" for of from TypeScript cause its more readable for me.

What do you guys suggest to use? Are there other points to use for of or better _.forEach

解决方案

I don't have any experience with typescript beyond my reading but I do have quite a bit of experience with ES6/ES2015. for of was and still is part of the ES2015 spec which was finalized. I would read this article on for of from MDN.

Here are some similarities and differences of for of and forEach (and these are just as far as I have found and know of currently):

  • forEach in lodash works on collections that are Arrays, Objects, or Strings.

  • native forEach works on Arrays, Maps, and Sets.

  • for of works on all Iterables: Arrays, Strings, TypedArrays, Maps, Sets, DOM collections, and generators.

I would read this chapter on for of from Exploring ES6 (Exploring ES6 is a great read. It's very thorough. It's free online as well.) Some things from it that stand out to me as different about for of that aren't in forEach.

break and continue work inside for-of loops

break and continue aren't exposed in forEach. The closest thing you can get to continue in forEach is using return which is actually pretty much the same thing. As for break though I see no alternative (but don't discount lodash, because most things that need breaks like finding and returning a single item are already covered in much of lodash's library).

It should also be noted that the await keyword from async/await is usable inside for of where as forEach makes it quite a bit harder to stop the surrounding block from waiting on Promises awaited within the forEach block however it is possible to use forEach although using a map or reduce may make awaiting much simpler than forEach (depending on your familiarity with those functions). Below is three separate implementations of awaiting promises both sequentially and in parallel using, for of, forEach, and reduce and map just to see the possible differences.

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");
});

With Object.entries which arrived in ES2017 you can even iterate objects own enumerable properties and values with ease and accuracy. If you want to use it now you can with one of the polyfills here. Here's an example of what that would look like.

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

and here's an implementation with a quick polyfill I wrote. You would normally use array destructuring as well which would seperate the key and value into it's own variables like this:

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"
}

You can also use Object.entries with forEach like so:

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"
});

forEach's first argument defaults to the type of functionality you would get from let in a for or for of loop which is a good thing. What I mean by that is if there is anything asynchronous going on inside the variable for that iteration will be scoped to just the particular part of that loop. This property of forEach is not really to do with let, but with scope and closures of functions in JavaScript, and the alternative is due to their not being block scoping. For example see what happens here when var is used:

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

for(var item of arr) {
  setTimeout(() => {
    console.log(item);
  }, 100);
}

As opposed to when let or foreach is used.

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

Again, I will note the difference between using var and using let or foreach. The difference is that var's variable is hoisted up to the top of the function scope (or file if it's not in a function) and then the value is reassigned for that whole scope, so the loop reaches its end and assigns item for the last time and then every settimeout function logs that last item. Whereas with let and foreach the variable item does not get overwritten, because item is scoped to the block (when let is used) or the function (when foreach is used).

Between forEach and for of you just need to decide which one is best for the current job (e.g. Do you need breaks or need to use Maps, Sets or Generators use for of). Besides that I feel like there aren't particularly strong reasons for either on collections they both operate on with their core functionalities. Also when dealing with collections that can use either forEach or for of it's mainly just up to personal preference as they do the same thing at about the same speed (and the speeds could change at any time according to the interpreter). I feel the particular advantages of lodash are for its other various functions which could actually save you a lot of time from writing the code yourself like map, reduce, filter, and find. Since you feel most comfortable writing for of I suggest you continue writing it that way but once you start writing in lodash using its other functions you may start to feel more comfortable writing it the lodash way.

Edit:

Looking over your code I noticed an error with your list creation. At the end you just had .split() and you should have had .split(","). You were creating a list of length 1 of the whole string and iterating one time on that string that is why the bench marks were so similar. I reran the tests. Here they are. I still wouldn't worry about the performance that much it seems to change every time it's ran.

这篇关于我们应该在TypeScript中使用_.foreach()还是更好的for循环本机for的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆