网站首页 > 知识剖析 正文
当我们开始学习FP函数式编程的时候,彷佛是从金庸武侠的山洞里,因缘巧合撞上武林秘籍。从此,再也不会陷入到种种 loop 以及 nested-loop的 dirty-details之中了。
前文我们介绍了 array.reduce 方法 帮你精通JS:神奇的array.reduce方法的10个案例 乃是 函数编程的第一式。第一式是基础,是我们思考的起点。后面的招数都从此演化出来,array.map就是从array.reduce演化出来的第二式。
一、array.map 方法概览
map() 方法创建一个新数组,其结果是该数组中的每个元素是调用一次提供的函数后的返回值。
const array1 = [1, 4, 9, 16];
// pass a function to map
const map1 = array1.map(x => x * 2);
console.log(map1);
// expected output: Array [2, 8, 18, 32]
二、array.map 的语法
基本语法为:
const array1 = [1, 4, 9, 16];
// pass a function to map
const map1 = array1.map(x => x * 2);
console.log(map1);
// expected output: Array [2, 8, 18, 32]
参数
- callback 生成新数组元素的函数,使用三个参数:
- - currentValue
- callback 数组中正在处理的当前元素。
- - index可选
- callback 数组中正在处理的当前元素的索引。
- - array可选 map 方法调用的数组。
- thisArg可选 执行 callback 函数时值被用作this。
与array.reduce相比较,少了accumulator一项,因为reduce是基础,从reduce里将map实现出来。
返回值
一个由原数组每个元素执行回调函数的结果组成的新数组。
三、描述
map 方法会给原数组中的每个元素都按顺序调用一次 callback 函数。callback 每次执行后的返回值(包括 undefined)组合起来形成一个新数组。 callback 函数只会在有值的索引上被调用;那些从来没被赋过值或者使用 delete 删除的索引则不会被调用。
因为map是pure-fuction,生成一个新数组,并且没有 side-effect。当你不打算使用返回的新数组却使用map是违背设计初衷的,请用forEach或者for-of替代。你不该使用map: A)你不打算使用返回的新数组,或/且 B) 你没有从回调函数中返回值。
callback 函数会被自动传入三个参数:数组元素,元素索引,原数组本身。
如果 thisArg 参数提供给map,则会被用作回调函数的this值。否则undefined会被用作回调函数的this值。this的值最终相对于callback函数的可观察性是依据the usual rules for determining the this seen by a function决定的。
map 不修改调用它的原数组本身(当然可以在 callback 执行时改变原数组),换言之乃是pure-function。
map 方法处理数组元素的范围是在 callback 方法第一次调用之前就已经确定了。调用map方法之后追加的数组元素不会被callback访问。如果存在的数组元素改变了,那么传给callback的值是map访问该元素时的值。在map函数调用后但在访问该元素前,该元素被删除的话,则无法被访问到。
根据规范中定义的算法,如果被map调用的数组是离散的,新数组将也是离散的保持相同的索引为空。
案例01.求数组中每个元素的平方根
下面的代码创建了一个新数组,值为原数组中对应数字的平方根。
var numbers = [1, 4, 9];
var roots = numbers.map(Math.sqrt);
// roots的值为[1, 2, 3], numbers的值仍为[1, 4, 9]
案例02.使用 map 重新格式化数组中的对象
以下代码使用一个包含对象的数组来重新创建一个格式化后的数组。
var kvArray = [{key: 1, value: 10},
{key: 2, value: 20},
{key: 3, value: 30}];
var reformattedArray = kvArray.map(function(obj) {
var rObj = {};
rObj[obj.key] = obj.value;
return rObj;
});
// reformattedArray 数组为: [{1: 10}, {2: 20}, {3: 30}],
// kvArray 数组未被修改:
// [{key: 1, value: 10},
// {key: 2, value: 20},
// {key: 3, value: 30}]
案例03. 使用一个包含一个参数的函数来mapping(构建)一个数字数组
下面的代码表示了当函数需要一个参数时map的工作方式。当map循环遍历原始数组时,这个参数会自动被分配成数组中对应的每个元素。
var numbers = [1, 4, 9];
var doubles = numbers.map(function(num) {
return num * 2;
});
// doubles数组的值为: [2, 8, 18]
// numbers数组未被修改: [1, 4, 9]
案例04. 一般的map 方法
下面的例子演示如何在一个 String 上使用 map 方法获取字符串中每个字符所对应的 ASCII 码组成的数组:
var map = Array.prototype.map
var a = map.call("Hello World", function(x) {
return x.charCodeAt(0);
})
// a的值为[72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100]
案例05. Mapping 含 undefined的数组
当返回undefined 或没有返回任何内容时:
var numbers = [1, 2, 3, 4];
var filteredNumbers = numbers.map(function(num, index) {
if(index < 3) {
return num;
}
});
//index goes from 0,so the filterNumbers are 1,2,3 and undefined.
// filteredNumbers is [1, 2, 3, undefined]
// numbers is still [1, 2, 3, 4]
案例06 压轴案例
通常情况下,map 方法中的 callback 函数只需要接受一个参数,就是正在被遍历的数组元素本身。但这并不意味着 map 只给 callback 传了一个参数。这个思维惯性可能会让我们犯一个很容易犯的错误。
考虑下例:
["1", "2", "3"].map(parseInt);
我们期望输出 [1, 2, 3], 而实际结果是 [1, NaN, NaN].
parseInt 经常被带着一个参数使用, 但是这里接受两个。第一个参数是一个表达式而第二个是callback function的基, Array.prototype.map 传递3个参数:
- the element
- the index
- the array
第三个参数被parseInt忽视了, but not the second one, 但不是第二个。因此可能出现混淆。下面是迭代步骤的简明示例:
// parseInt(string, radix) -> map(parseInt(value, index))
/* first iteration (index is 0): */ parseInt("1", 0); // 1
/* second iteration (index is 1): */ parseInt("2", 1); // NaN
/* third iteration (index is 2): */ parseInt("3", 2); // NaN
下面让我们来讨论解决方案:
function returnInt(element) {
return parseInt(element, 10);
}
['1', '2', '3'].map(returnInt); // [1, 2, 3]
// Actual result is an array of numbers (as expected)
// Same as above, but using the concise arrow function syntax
['1', '2', '3'].map( str => parseInt(str) );
// A simpler way to achieve the above, while avoiding the "gotcha":
['1', '2', '3'].map(Number); // [1, 2, 3]
// But unlike parseInt(), Number() will also return a float or (resolved) exponential notation:
['1.1', '2.2e2', '3e300'].map(Number); // [1.1, 220, 3e+300]
// For comparison, if we use parseInt() on the array above:
['1.1', '2.2e2', '3e300'].map( str => parseInt(str) ); // [1, 2, 3]
一个map方法调用 parseInt 作为一个参数的等效输出运行如下:
var xs = ['10', '10', '10'];
xs = xs.map(parseInt);
console.log(xs); // 输出结果为
(3) [10, NaN, 2]
// Actual result of 10,NaN,2 may be unexpected based on the above description.
以上。
猜你喜欢
- 2024-11-14 vue.js正确使用百度地图的方案 vue百度地图自定义控件
- 2024-11-14 七爪源码:如何在 JavaScript 中将 JSON 转换为地图
- 2024-11-14 在地图上创建热力图的方法 怎么用热力图选址
- 2024-11-14 快速了解 ES6 的Map与WeakMap es6 map使用场景
- 2024-11-14 Vue3 + TS + Leafletjs 打造企业级原神大地图
- 2024-11-14 好东西一起用,Cesium.js真是让3D地图尖叫的神奇框架!
- 2024-11-14 为 Next.js 项目创建站点地图文件
- 2024-11-14 JavaScript 轻松获取 Map 中的某个 key
- 2024-11-14 何时使用 Map 来代替普通的 JS 对象
- 2024-11-14 看了几十篇论文实现了个专业算法,用threejs做了个人员定位系统
- 最近发表
- 标签列表
-
- xml (46)
- css animation (57)
- array_slice (60)
- htmlspecialchars (54)
- position: absolute (54)
- datediff函数 (47)
- array_pop (49)
- jsmap (52)
- toggleclass (43)
- console.time (63)
- .sql (41)
- ahref (40)
- js json.parse (59)
- html复选框 (60)
- css 透明 (44)
- css 颜色 (47)
- php replace (41)
- css nth-child (48)
- min-height (40)
- xml schema (44)
- css 最后一个元素 (46)
- location.origin (44)
- table border (49)
- html tr (40)
- video controls (49)