美团面试碰到面试官让我手写一个JS数组的map方法,其实功能很简单,但是里面有几个细节比较难把握:
- map方法有几个参数
- 如何拿到调用map的数组
- 如何修改原本数组的值
首先看一下我在网上找的别人的代码,除去前面异常的处理,核心代码还是第9到第11行。
Array.prototype.myMap = function (callback, thisArg) {
let length = this.length
let res = []
if (!Array.isArray(this)) throw new TypeError('this is not an array')
if (typeof callback !== 'function') throw new TypeError(callback + 'is not a function')
if (length === 0) {
return res
}
for (let i = 0; i < length; i++) {
res[i] = callback.call(thisArg, this[i], i, this)
}
return res
}
参数问题
对于第一个问题,从代码里就可以看到map方法有两个参数,一个回调函数,一个thisArg。
而回调函数本身也有三个参数:
-
element数组中当前正在处理的元素。 -
index正在处理的元素在数组中的索引。 -
array调用了map()的数组本身。
map方法的第二个参数thisArg表示在执行 callbackFn 时 this 的值,因此我们需要将这个参数绑定为回调函数的this值。
在上面的代码中,第9行代码为核心步骤,即调用回调函数并修改它的this值。具体来说,使用了call方法,第一个参数固定了回调函数this的值,后面几个参数对应于上面提到的回调函数的三个参数——element、index、array。
res[i] = callback.call(thisArg, this[i], i, this)
需要注意的是,如果想要用map的第二个参数设置回调函数的this,回调函数不能使用箭头函数的写法,因为箭头函数在使用call、bind等方法绑定this与参数时,this这个值的设置是不起作用的。
let arr = [1,2,3];
let obj = { a:1 }
arr.map( function(val){
console.log(this) // { a:1 }
},obj)
// 错误示例
arr.map( (val) => {
console.log(this) // window 对象
}, {a:1})
如何拿到数组
先来看一下这三行代码,使用了this[i]这样的写法,这说明在原型对象中可以使用this指代调用该方法的实例对象。
for (let i = 0; i < length; i++) {
res[i] = callback.call(thisArg, this[i], i, this)
}
其实原型对象里面放的是方法, 而这个方法里面的this 指向的是这个方法的调用者, 也就是这个实例对象,我们想要获取调用该方法的数组,直接使用this就好。
如何修改原数组
其实这个问题说的不对,因为map方法是创建一个新数组,而这个新数组由原数组中的每个元素都调用一次提供的函数后的返回值组成。
因此,想要拿到修改后的数组,直接在map方法中返回新的数组就好。
总结
map方法实现可能会遇到三个问题,如下——
- map方法有几个参数
- 如何拿到调用map的数组
- 如何修改原本数组的值
他们的答案可以简单概括为——
- 两个参数,回调函数
Fn与可选参数thisArg(作为回调函数执行时的this) - 方法中的
this指向的就是调用该方法的数组,可以直接用this[i]这种形式来代替数组 - map方法返回一个新数组,并不修改原本数组的值