javascript forEach方法有什么用途(该地图无法做到)?


90

我在map和foreach中看到的唯一区别map是返回一个数组而forEach不是。但是,我什至不了解forEach方法“ func.call(scope, this[i], i, this);” 的最后一行。例如,不为“ this”和“ scope”,指的同一个对象,而不是this[i]i参照在环路中的电流值?

我在另一篇文章中注意到有人说:“ forEach当您想根据列表的每个元素来做某事时使用。例如,您可能要在页面中添加内容。从本质上讲,当您想要“副作用”时,它非常有用。我不知道副作用是什么意思。

Array.prototype.map = function(fnc) {
    var a = new Array(this.length);
    for (var i = 0; i < this.length; i++) {
        a[i] = fnc(this[i]);
    }
    return a;
}

Array.prototype.forEach = function(func, scope) { 
    scope = scope || this; 
    for (var i = 0, l = this.length; i < l; i++) {
        func.call(scope, this[i], i, this); 
    } 
}

最后,除了像这样操作数字之外,javascript中这些方法还有什么实际用途(因为我们没有更新数据库):

alert([1,2,3,4].map(function(x){ return x + 1})); //this is the only example I ever see of map in javascript.

感谢您的答复。


像使用mapand 一样,如何找到本机JavaScript的函数定义forEach?我从Google那里获得的只是使用规范和教程。
WoodrowShigeru

另请参见语言不可知foreach和map之间是否有区别?
Bergi

Answers:


94

mapforEach您的示例之间的本质区别在于,该forEach操作对原始数组元素进行操作,而map显式地返回一个新数组作为结果。

随着forEach您对原始数组中的每个元素(可能有更改)采取一些措施。该forEach方法运行您为每个元素提供的功能,但不返回任何内容(undefined)。另一方面,map遍历数组,将函数应用于每个元素,并将结果作为新array发出

“副作用” forEach是更改了原始数组。“无副作用”的map意思是,在习惯用法中,原始数组元素不会更改;新数组是原始数组中每个元素的一对一映射-映射转换是您提供的功能。

没有数据库的事实并不意味着您不必对数据结构进行操作,毕竟,这是任何语言编程的本质之一。至于最后一个问题,数组不仅可以包含数字,还可以包含对象,字符串,函数等。


4
未来的笔记:这个答案实际上是胡说八道;.map可以做的所有.forEach事情(包括修改元素),它只是返回从迭代器函数构造的新列表。
特拉维斯·韦伯

6
回应过去:我谨表示不同意。.map()创建一个新数组,而不更改原始数组。您确实可以修改本身正在映射的数组,但是如果不是毫无意义的话,那至少是非惯用的。.forEach(),尽管类似,但将其功能应用于每个元素,但始终返回undefined。尽管如此,OP还询问了自己添加到数组原型中的特定功能。过去这里没有ES5。
肯·雷德勒

4
仍然没有forEach您无法做的事情map。您可能根本就不在乎来自的返回值,map并且会有一个forEach。区别在于,map对于不想基于结果创建新数组的任务使用超级无效。因此,对于这些,请使用forEach

2
@poke:..同样,您可以使用简单的旧for循环执行任何所需的操作。我不同意“有效性”上的区别,但我也不认为有人在争论一个人拥有某种魔术,而另一种却无法实现。尽管实际上存在一件事map不能做:不返回数组。有一个在其JS辜负了其他语言的预期为像功能最爱的行为价值mapreducefilter,等如,你可以实现reduceRight使用类似积木,但为什么不使用reduceRight
肯·雷德勒

1
@ChinotoVokro,您的示例通过在返回的对象内部分配来显式地更改原始数组。这就是我们所说的确切事实,确实是可能的,但令人困惑且不习惯。通常,您只需返回新值,而不是将其分配给原始数组即可:b.val = b.val**2 a=[{val:1},{val:2},{val:3},{val:4}]; a.map((b)=>{b.val**2}); JSON.stringify(a);
Ken Redler

66

这两种方法之间的主要区别是概念和风格:您可以使用forEach,当你想要做的事,以数组中的每个元素(做“和”什么是你引用的“副作用”的意思后,我想) ,而map在要复制和转换数组的每个元素时使用(而无需更改原始元素)。

因为无论mapforEach调用每个项目的功能在阵列中,且功能被用户定义的,几乎没有什么可以与一个而不是与其他做。也许很丑陋,但是可以map用来就地修改数组和/或对数组元素做一些事情:

var a = [{ val: 1 }, { val: 2 }, { val: 3 }];
a.map(function(el) {
    el.val++; // modify element in-place
    alert(el.val); // do something with each element
});
// a now contains [{ val: 2 }, { val: 3 }, { val: 4 }]

但是对于您的使用意图而言,它更干净,更明显forEach

var a = [{ val: 1 }, { val: 2 }, { val: 3 }];
a.forEach(function(el) { 
    el.val++;
    alert(el.val);
});

尤其是在现实世界中通常el是有用的人类可读变量的情况下:

cats.forEach(function(cat) { 
    cat.meow(); // nicer than cats[x].meow()
});

同样,您可以轻松地使用它forEach来创建一个新数组:

var a = [1,2,3],
    b = [];
a.forEach(function(el) { 
    b.push(el+1); 
});
// b is now [2,3,4], a is unchanged

但使用起来更干净map

var a = [1,2,3],
    b = a.map(function(el) { 
        return el+1; 
    });

还要注意的是,由于要map创建一个新的数组,所以当您需要的只是迭代时,它可能至少会产生一些性能/内存方面的损失,尤其是对于大型数组-请参见http://jsperf.com/map-foreach

至于为什么要使用这些功能,它们在需要使用javascript进行数组操作时非常有用,即使在几乎任何时候,这种操作(即使我们只是在浏览器环境中谈论javascript)您正在访问的代码中没有手工写下的数组。您可能正在处理页面上的DOM元素数组,或者从AJAX请求中提取的数据,或者由用户以表单形式输入的数据。我遇到的一个常见示例是从外部API提取数据,您可能希望使用该API map将数据转换为所需的格式,然后使用forEach它遍历新数组以将其显示给用户。


我看到人们.map()一直在用时间修改内联元素。我印象中这是使用.map过度的主要好处.forEach
弯曲的肉汁

1
@chovy我相信您应该根据是否要生成一系列结果进行选择。.map可以修改元素,是的,但是它将创建您不需要的数组。您还应该考虑如何选择一个或另一个让读者猜测您是否有副作用
leewz 16-10-26

16

投票的答案(来自Ken Redler)具有误导性。

副作用在计算机科学装置,其的功能/方法涂改一个全局状态的性质[维基]。从狭义上讲,这可能还包括从全局状态而不是自变量中读取。在命令式或OO式编程中,大多数情况下会出现副作用。而且您可能正在利用它而没有意识到。

forEach和之间的显着区别mapmap分配内存并存储返回值,而forEach将其丢弃。有关更多信息,请参见emca规范

至于人们说forEach要使用副作用时使用的原因是的返回值forEach始终为undefined。如果没有副作用(不更改全局状态),则该功能仅浪费CPU时间。优化的编译器将消除此代码块,并将其替换为最终值(undefined)。

顺便提一下,JavaScript对副作用没有任何限制。您仍然可以在其中修改原始数组map

var a = [1,2,3]; //original
var b = a.map( function(x,i){a[i] = 2*x; return x+1} );
console.log("modified=%j\nnew array=%j",a,b);
// output:
// modified=[2,4,6]
// new array=[2,3,4]

1
这个问题及其答案和评论总是很有趣,每两年回顾一次。就内存分配而言,在JS中实现映射的方法是另一个不错的角度。
肯·雷德勒

6

这是一个美丽的问题,带有意外的答案。

以下是基于的官方描述Array.prototype.map()

没有什么forEach()可以做到这一点map()做不到。也就是说,map()是一个严格的超集forEach()

尽管map()通常用于创建新阵列,但可以用于更改当前阵列。以下示例说明了这一点:

var a = [0, 1, 2, 3, 4], mapped = null;
mapped = a.map(function (x) { a[x] = x*x*x; return x*x; });
console.log(mapped); // logs [0, 1, 4, 9, 16]  As expected, these are squares.
console.log(a); // logs [0, 1, 8, 27, 64] These are cubes of the original array!!

在上面的示例中,a方便地设置a[i] === ii < a.length。即使这样,它也展示了的能力map(),尤其是它改变其调用数组的能力。

注意1:
官方描述暗示map()甚至可能更改其所调用数组的长度!但是,我看不出这样做的理由。

注意2:
虽然map()map是的超集forEach()forEach()但仍应在需要更改给定数组的地方使用。这使您的意图很明确。

希望这会有所帮助。


8
实际上,forEach可以做的一件事是map不能做的-不返回数组。
Antti Haapala 2014年

1
您还可以使用映射函数的第三个参数来更改目标数组,而不是作用域变量:mapped = a.map(function (x, i, arr) { arr[i] = x * x * x; return x * x; });.
pdoherty926

@ pdoherty926正确,但是也可以a.forEach(function(x, i, arr) { ...,当您打算将每个原始项目突变而不是将值从一个数组映射到另一个数组时,这再次是从概念上更正确的方式
conny 2016年

@conny:同意。另外,请参阅针对类似问题的答案
Sumukh Barve

3

您可以map像以前那样使用forEach

但是,它将做的事情比必须做的更多。

scope可以是任意对象;并非一定如此this

至于map和的实际用途forEach,以及询问是否对forwhile循环有实际用途。


但是为什么在这里同时调用“作用域”和“ this”:func.call(scope,this [i],i,this); 范围是否不是等于当前对象“ this”的参数?
JohnMerlino 2010年

不可以,它可以等于当前对象。对象本身作为第三个参数传递给数组。scope = scope || this表示“如果scope是虚假的(未定义,null,false等),请将范围设置为this替代并继续”。
wombleton 2010年

当不等于这个示例时,您可以将我链接到一个示例吗?
JohnMerlino 2010年

developer.mozilla.org/en/Core_JavaScript_1.5_Reference/…在“使用对象方法打印数组的内容”下有一个
wombleton 2010年

1

尽管前面的所有问题都是正确的,但我肯定会做出不同的区分。使用mapforEach可能意味着意图。

map当我只是以某种方式转换现有数据时,我喜欢使用(但要确保原始数据不变)。

我喜欢forEach在修改集合时使用。

例如,

var b = [{ val: 1 }, { val: 2 }, { val: 3 }];
var c = b.map(function(el) {
    return { val: el.val + 1 }; // modify element in-place
});
console.log(b);
//  [{ val: 1 }, { val: 2 }, { val: 3 }]
console.log(c);
//  [{ val: 3 }, { val: 4 }, { val: 5 }]

我的经验法则是确保map始终在创建一些新的对象/值以返回源列表的每个元素并返回它,而不是仅对每个元素执行一些操作。

除非您真的有需要修改现有列表,否则就没有必要对其进行修改,而是更适合功能/不变编程风格。


1

TL; DR答案-

map总是返回另一个数组。

forEach不会。您可以自行决定要做什么。如果需要,返回一个数组,否则返回其他数组。

在某些情况下需要灵活性。如果不是您要处理的内容,请使用map。


0

再一次,我觉得自己像个死灵法师,对5年前提出的一个问题做出了回应(很高兴,最近的活动很活跃,所以我不是唯一打扰死者的人:)。

其他人已经发布了有关功能之间差异的主要问题。但对于...

除了像这样处理数字之外,javascript中这些方法还有什么实际用途(因为我们没有更新数据库):

...你应该问这很有趣。就在今天,我编写了一段代码,使用map进行转换,将正则表达式中的多个值分配给多个变量。它用于将非常复杂的基于文本的结构转换为可视化数据...但是为了简单起见,我将提供一个使用日期字符串的示例,因为每个人可能都比较熟悉(尽管如果我的问题实际上是关于日期的, ,而不是地图,我会使用Date-object,它会出色地完成这项工作)。

const DATE_REGEXP = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})\.(\d{3})Z$/;
const TEST_STRING = '2016-01-04T03:20:00.000Z';

var [
    iYear,
    iMonth,
    iDay,
    iHour,
    iMinute,
    iSecond,
    iMillisecond
    ] = DATE_REGEXP
        // We take our regular expression and...
        .exec(TEST_STRING)
        // ...execute it against our string (resulting in an array of matches)...
        .slice(1)
        // ...drop the 0th element from those (which is the "full string match")...
        .map(value => parseInt(value, 10));
        // ...and map the rest of the values to integers...

// ...which we now have as individual variables at our perusal
console.debug('RESULT =>', iYear, iMonth, iDay, iHour, iMinute, iSecond, iMillisecond);

所以...虽然这只是一个示例-仅对数据进行了非常基本的转换(仅出于示例目的)...在没有map的情况下完成此操作将是一件繁琐的工作。

当然,它是用JavaScript版本编写的,我认为目前还没有(至少是完全)没有太多的浏览器支持,但是-我们正在实现。如果我需要在浏览器中运行它,我相信它会很好地转换。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.