javascript forEach方法有什么用(地图不能做)?

时间:2010-06-13 23:58:54

标签: javascript arrays dictionary foreach prototype

我在map和foreach中看到的唯一区别是map返回一个数组而forEach没有。但是,我甚至不理解forEach方法“func.call(scope, this[i], i, this);”的最后一行。例如,引用相同对象的“this”和“scope”不是this[i]i引用循环中的当前值吗?< / p>

我在另一篇文章中注意到有人说“当你想根据列表的每个元素做某事时,请使用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.

感谢您的回复。

8 个答案:

答案 0 :(得分:90)

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

使用forEach,您正在对原始数组中的每个元素采取一些操作 - 并且可以选择更改 - 。 forEach方法运行您为每个元素提供的函数,但不返回任何内容(undefined)。另一方面,map遍历数组,将函数应用于每个元素,将结果作为新数组发送

forEach的“副作用”是原始数组正在被更改。 map的“无副作用”意味着,在惯用法中,原始数组元素更改;新数组是原始数组中每个元素的一对一映射 - 映射变换是您提供的函数。

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

答案 1 :(得分:66)

这两种方法的主要区别在于概念性和风格性:当您想要 时,使用forEach数组的每个元素(做“with”是你引用的“副作用”所指的帖子,我认为),而当你想要复制和转换数组的每个元素时,你使用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中提取数据,您可能希望使用map将数据转换为所需的格式,然后使用forEach按顺序迭代新数组将其显示给您的用户。

答案 2 :(得分:13)

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

计算机科学中的副作用意味着函数/方法的属性会改变全局状态[wiki]。从狭义上讲,这也可能包括从全球国家而不是从争论中读取。在命令式或OO编程中,大多数时候都会出现副作用。你可能在没有意识到的情况下使用它。

forEachmap之间的显着差异在于map分配内存并存储返回值,而forEach则抛出它。有关详细信息,请参阅emca spec

至于人们在需要副作用时使用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]

答案 3 :(得分:5)

这是一个意想不到的答案的漂亮问题。

以下内容基于official description of Array.prototype.map()

forEach()可以执行<{>} <{1}}无法做到的事情。也就是说,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] === i。即便如此,它也展示了i < a.length的强大功能,特别是它能够改变调用它的数组。

<强>注1:
官方说明暗示map()甚至可能会更改长度调用它的数组!但是,我看不出(很好)这样做的理由。

<强>注2:
虽然map()地图是map()的超集,但仍应在需要更改给定数组的地方使用forEach()。这使你的意图清晰。

希望这会有所帮助。

答案 4 :(得分:3)

您可以map使用forEach

然而,

它会做的比它更多。

scope可以是任意对象;它绝不一定是this

至于mapforEach是否有实际用途,以及询问forwhile循环是否有实际用途。

答案 5 :(得分: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总是创建一些新的对象/值来为源列表的每个元素返回并返回而不仅仅是执行一些对每个元素的操作。

除非您真正需要修改现有列表,否则在适当位置修改它并没有多大意义,并且更适合函数/不可变编程样式。

答案 6 :(得分: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);

所以...虽然这只是一个例子 - 并且只对数据做了一个非常基本的转换(仅仅是为了举例)...没有地图这样做会是一件非常繁琐的工作

当然,它是用JavaScript版本编写的,我不认为太多浏览器支持(至少完全)但是 - 我们正在那里。如果我需要在浏览器中运行它,我相信它会很好地传播。

答案 7 :(得分:0)

TL; DR回答 -

map始终返回另一个数组。

forEach没有。由您来决定它的作用。如果你愿意,可以返回一个数组,否则返回其他内容。

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