我们应该在TypeScript中使用_.foreach()还是更好的for循环本机for [英] Should we use _.foreach() or better the native for of loop in TypeScript
问题描述
我刚刚开始在一个使用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 of
和forEach
的一些相似之处和不同之处(据我所知,目前为止):
-
lodash中的
-
forEach
适用于数组,对象或对象的集合 字符串. -
本机
forEach
适用于数组,地图和集合. -
for of
适用于所有 Iterables :数组,字符串,TypedArrays, 地图,集合,DOM集合和生成器.
我会从探索ES6 (<阅读.它非常彻底.它也是免费的在线.)其中一些与for of
有所不同的东西对我来说很明显,而forEach
中却没有.
在for-of循环中中断并继续工作
break
和continue
在forEach
中没有公开.您可以在forEach中最接近continue
的地方使用return
,实际上这几乎是同一件事.至于break
,虽然我没有其他选择(但不要打折lodash,因为lodash的很多库中已经涵盖了大多数需要休息的东西,例如查找和返回单个项目).
还应注意,async/await中的await
关键字在for of
内可用,因为forEach使得阻止周围的块等待forEach块中已等待的Proposes相当困难.可以使用forEach
,尽管使用map
或reduce
可能比forEach更加简单(取决于您对这些功能的熟悉程度).下面是使用for of
,forEach
和reduce
和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");
});
使用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.entries
与forEach
结合使用,例如:
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
的第一个参数默认为在for
或for 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
与使用let
或foreach
之间的区别.区别在于将var的变量提升到函数作用域的顶部(如果不在函数中,则文件被悬挂),然后为整个作用域重新分配值,这样循环就结束了并为最后一次分配item
,然后最后每个item
的每个settimeout
功能日志.而对于let
和foreach
,变量item
不会被覆盖,因为item
的作用域是块(使用let时)或函数(使用foreach时).
在forEach
和for of
之间,您只需要确定哪个最适合当前工作(例如,您是否需要break
或需要使用Maps,Sets或Generators使用for of
).除此之外,我觉得这两个集合都没有核心功能的理由并没有特别强烈的理由.同样,在处理可以使用forEach
或for 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 break
s 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屋!