我之前已就How to get number of response of JSON?回答了一个问题,我建议他们使用map
函数而不是使用for
循环,但有人评论说{{1}不是用于循环而是用.map
代替。
在forEach
循环上使用map
是否有任何缺点?
我也对此进行了研究,发现site表示for
> map
。
答案 0 :(得分:5)
Map用于将数组中的每个元素转换为另一个表示形式,并以新的顺序返回结果。但是,由于为每个项调用了函数,因此您可以进行任意调用并且不返回任何内容,从而使其像forEach
一样,尽管严格来说它们并不相同。
正确使用map(将值数组转换为另一种表示形式):
var source = ["hello", "world"];
var result = source.map(function(value) {
return value.toUpperCase();
});
console.log(result); // should emit ["HELLO, "WORLD"]
无意中使用.map
进行迭代(语义错误):
var source = ["hello", "world"];
// emits:
// "hello"
// "world"
source.map(function(value) {
console.log(value);
});
第二个示例在技术上是有效的,它会编译并运行,但这不是map
的预期用途。
"谁在乎呢,是否符合我的要求?"可能是你的下一个问题。首先,map
忽略索引处具有指定值的项。此外,因为map
期望返回结果,所以它正在做额外的事情,从而为一个简单的过程分配更多的内存和处理时间(尽管非常微小)。更重要的是,它可能会让您自己或其他开发人员难以维护您的代码。
答案 1 :(得分:2)
map()方法创建一个新数组,其中包含调用a的结果 为此数组中的每个元素提供了函数。
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map
map
按顺序为数组中的每个元素调用一次提供的回调函数,并从结果中构造一个新数组。仅对已分配值的数组的索引(包括undefined)调用回调。它不会被调用缺少数组的元素(即,从未设置过的索引,已删除的索引或从未赋值的索引)。
回调:元素的值,元素的索引和被遍历的Array对象。
如果提供了thisArg
参数进行映射,则会在调用时将其传递给回调,以用作此值。否则,将传递未定义的值以用作其此值。最终通过回调可观察到的这个值是根据确定函数所见的通常规则来确定的。
map不会改变调用它的数组(尽管回调,如果被调用,可能会这样做)。
在第一次调用回调之前设置map处理的元素范围。回调后将不会访问在映射调用开始后附加到数组的元素。如果更改或删除了数组的现有元素,则传递给回调的值将是时间映射访问它们时的值;删除的元素不会被访问。
来自MDN的参考:
// Production steps of ECMA-262, Edition 5, 15.4.4.19
// Reference: http://es5.github.io/#x15.4.4.19
if (!Array.prototype.map) {
Array.prototype.map = function(callback, thisArg) {
var T, A, k;
if (this == null) {
throw new TypeError(' this is null or not defined');
}
// 1. Let O be the result of calling ToObject passing the |this|
// value as the argument.
var O = Object(this);
// 2. Let lenValue be the result of calling the Get internal
// method of O with the argument "length".
// 3. Let len be ToUint32(lenValue).
var len = O.length >>> 0;
// 4. If IsCallable(callback) is false, throw a TypeError exception.
// See: http://es5.github.com/#x9.11
if (typeof callback !== 'function') {
throw new TypeError(callback + ' is not a function');
}
// 5. If thisArg was supplied, let T be thisArg; else let T be undefined.
if (arguments.length > 1) {
T = thisArg;
}
// 6. Let A be a new array created as if by the expression new Array(len)
// where Array is the standard built-in constructor with that name and
// len is the value of len.
A = new Array(len);
// 7. Let k be 0
k = 0;
// 8. Repeat, while k < len
while (k < len) {
var kValue, mappedValue;
// a. Let Pk be ToString(k).
// This is implicit for LHS operands of the in operator
// b. Let kPresent be the result of calling the HasProperty internal
// method of O with argument Pk.
// This step can be combined with c
// c. If kPresent is true, then
if (k in O) {
// i. Let kValue be the result of calling the Get internal
// method of O with argument Pk.
kValue = O[k];
// ii. Let mappedValue be the result of calling the Call internal
// method of callback with T as the this value and argument
// list containing kValue, k, and O.
mappedValue = callback.call(T, kValue, k, O);
// iii. Call the DefineOwnProperty internal method of A with arguments
// Pk, Property Descriptor
// { Value: mappedValue,
// Writable: true,
// Enumerable: true,
// Configurable: true },
// and false.
// In browsers that support Object.defineProperty, use the following:
// Object.defineProperty(A, k, {
// value: mappedValue,
// writable: true,
// enumerable: true,
// configurable: true
// });
// For best browser support, use the following:
A[k] = mappedValue;
}
// d. Increase k by 1.
k++;
}
// 9. return A
return A;
};
}
答案 2 :(得分:1)
“地图不用于循环”这个短语可能有点不准确,因为当然map
取代了for
- 循环。
评论者说的是,当 想要循环时,您应该使用forEach
,并且当您想要通过应用操作来收集结果时使用map
每个数组元素。这是一个简单的例子:
> a = [10, 20, 30, 40, 50]
[ 10, 20, 30, 40, 50 ]
> a.map(x => x * 2)
[ 20, 40, 60, 80, 100 ]
> count = 0;
0
> a.forEach(x => count++)
undefined
> count
5
此处map
保留将函数应用于每个元素的结果。当您关心操作的每个单独结果时,您map
。相反,在计算数组中元素数量的情况下,我们不需要生成新数组。我们只关心单结果!
因此,如果只是想要循环,请使用forEach
。如果您需要收集所有结果,请使用map
。
答案 3 :(得分:1)
考虑map
的最佳方式是将其视为&#34;功能性&#34; for a loop with some superpowers。
当您在阵列上调用.map
时,会发生两件事。
.map
函数返回您为map提供的函数返回的所有值的数组。 让我们看一个例子。
var collection = [1, 2, 3, 4];
var collectionTimesTwo = collection.map(function (item) {
return item * 2;
});
console.log(collection) // 1, 2, 3, 4
console.log(collectionPlusOne) // 2, 4, 6, 8
在第一行,我们定义了我们的原始集合,1到4。
在接下来的几行中,我们执行map
。这将循环遍历collection
中的每个项目,并将每个item
传递给该函数。该函数返回item
乘以2
。这最终会生成一个新数组collectionTimesTwo
- 将数组中每个项目乘以2的结果。
让我们再看一个例子,说我们有一个单词集合,我们希望用map
var words = ['hello', 'world', 'foo', 'bar'];
var capitalizedWords = words.map(function (word) {
return word.toUpperCase();
})
console.log(words) // 'hello', 'world', 'foo', 'bar'
console.log(capitalizedWords) // 'HELLO', 'WORLD', 'FOO', 'BAR'
查看我们要去哪里?
这让我们在功能上更有效,而不是像以下
var words = ['hello', 'world', 'foo', 'bar'];
var capitalizedWords = [];
for (var i = 0; i < words.length; i++) {
capitalizedWords[i] = words[i].toUpperCase();
}
答案 4 :(得分:1)
有些事情可以客观地说,无视主观部分。
什么是清晰简洁的用法?如果我使用map()
任何阅读代码的人都假设我正在做它所说的:以某种方式映射值。作为查询表,计算或其他。我取值并返回(相同数量)转换的值。
当我forEach()
时,我会理解我将使用所有值作为输入来做某事但我没有做任何转换而没有返回任何内容。
链接只是副作用,而不是使用其中一个的原因。除非你进行映射,否则你的循环多久会返回一些你可以或想要在循环中重用的内容?
性能。是的,它可能是微优化,但是如果您不打算使用它,为什么要使用一个能够收集并返回数组的函数?
您链接的博客文章非常混乱。它讨论for
使用更多内存,然后推荐map()
,因为它很酷,即使它使用更多内存并且性能更差。
另外,作为一则轶事,链接的测试在我的浏览器上比for
运行forEach
更快。所以不能说明客观的表现。
即使意见不应该依赖于SO,我相信这是一般意见:使用为此使用的方法和功能。这意味着for
或forEach()
用于循环,map()
用于映射。