作为一名合格的前端,js中的map是几乎每天都会写的函数,必须把它的来龙去脉
搞清楚,把它嚼碎后慢慢咀嚼然后咽下去,再配一杯82年的白开水 🛫️
打开 mdn 搜索一下 Array.prototype.map 可以看到 map 有两个形参,
第一个参数是一个回调函数,第二个参数是 this 指向,在第一个参数的函数中又包括三个行参,分别是元素项、索引、原数组;
map 的返回值是一个 新 的数组。  
知道了怎么用,那我们大概来手写一个 map 来试试:
Array.prototype.my_map = function(fn, self){
    let newArray = []
    // 判断 fn 是否是函数 否则报错(为了跟 map 一样,报错尽量跟它一摸一样)
    if(typeof fn !== 'function') throw `TypeError: ${fn} is not a function`
    for(let i = 0; i < this.length; i++){
        newArray.push(fn.call(self, this[i], i, this))
    }
    return newArray
}
// 测试1 通过😄
[1].map(v=>v+1)    // [2]
[1].my_map(v=>v+1) // [2]
// 测试2 通过😊 提示语是类似了,这边原生的map会多提示具体报错的行数
[1].map('ss')     // Uncaught TypeError: ss is not a function
                  // at Array.map (<anonymous>)
                  // at <anonymous>:1:5
[1].my_map('ss')  // Uncaught TypeError: ss is not a function简单的测试了一下,基本没什么问题 🎉,还是来系统测试一遍看看,js的类型都有以下
内容,我们来依次测试看看:  
- null
- undefined
- Boolean
- Number
- BigInt
- String
- Object
- Array
- Function
- Symbol
- Date
// null ✅
[1].map(null)    // Uncaught TypeError: null is not a function
                 // at Array.map (<anonymous>)
                 // at <anonymous>:1:5
[1].my_map(null) // Uncaught TypeError: null is not a function
// undefined ✅
[1].map(undefined)    // Uncaught TypeError: undefined is not a function
                      // at Array.map (<anonymous>)
                      // at <anonymous>:1:5
[1].my_map(undefined) // Uncaught TypeError: undefined is not a function
// Boolean ✅
[1].map(Boolean)    // [true]
[1].my_map(Boolean) // [true]
[1].map(true)       // Uncaught TypeError: true is not a function
                    // at Array.map (<anonymous>)
                    // at <anonymous>:1:5
[1].my_map(true)    // Uncaught TypeError: true is not a function
[1].map(false)      // Uncaught TypeError: false is not a function
                    // at Array.map (<anonymous>)
                    // at <anonymous>:1:5
[1].my_map(false)   // Uncaught TypeError: false is not a function
// Number ✅
[1].map(Number)    // [1]
[1].my_map(Number) // [1]
[1].map(1)         // Uncaught TypeError: 1 is not a function
                   // at Array.map (<anonymous>)
                   // at <anonymous>:1:5
[1].my_map(1)      // Uncaught TypeError: 1 is not a function
// BigInt ✅
[1].map(BitInt)    // Uncaught ReferenceError: BitInt is not defined
                   // at <anonymous>:1:9
[1].my_map(BitInt) // Uncaught ReferenceError: BitInt is not defined
                   // at <anonymous>:1:12
                   
[1].map(1n)        // Uncaught TypeError: 1 is not a function
                   // at Array.map (<anonymous>)
                   // at <anonymous>:1:5
[1].my_map(1n)     // Uncaught TypeError: 1 is not a function
// String ✅
[1].map(String)    // ['1']
[1].my_map(String) // ['1']
[1].map('a')       // Uncaught TypeError: a is not a function
                   // at Array.map (<anonymous>)
                   // at <anonymous>:1:5
[1].my_map('a')    // Uncaught TypeError: a is not a function
// Object ❌
[1].map(Object)    // [Number]
[1].my_map(Object) // [Number]
[1].map({})        // Uncaught TypeError: #<Object> is not a function
                   // at Array.map (<anonymous>)
                   // at <anonymous>:1:5
[1].my_map({})     // Uncaught TypeError: [object Object] is not a function
// Array ❌
[1].map(Array)    // [Array(3)]
[1].my_map(Array) // [Array(3)]
[1].map([])       // Uncaught TypeError: [object Array] is not a function
                  // at Array.map (<anonymous>)
                  // at <anonymous>:1:5
[1].my_map([])    // Uncaught TypeError:  is not a function
// Function ✅
[1].map(Function)    // Uncaught EvalError: Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of script in the following Content Security Policy directive: "script-src 'self'".
                     // at Function (<anonymous>)
                     // at Array.map (<anonymous>)
                     // at <anonymous>:1:5
[1].my_map(Function) // Uncaught EvalError: Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of script in the following Content Security Policy directive: "script-src 'self'".
                     // at Function (<anonymous>)
                     // at Array.my_map (<anonymous>:6:26)
                     // at <anonymous>:1:5
[1].map(v=>v*2)      // [2]
[1].my_map(v=>v*2)   // [2]
// Symbol ❌
[1].map(Symbol)            // [Symbol(1)]
[1].my_map(Symbol)         // [Symbol(1)]
[1].map(Symbol('name'))    // Uncaught TypeError: Symbol(name) is not a function
                           // at Array.map (<anonymous>)
                           // at <anonymous>:1:5
[1].my_map(Symbol('name')) // Uncaught TypeError: Cannot convert a Symbol value to a string
                           // at Array.my_map (<anonymous>:4:54)
                           // at <anonymous>:1:5
                           
// Date ❌
[1].map(Date)          // ['Wed Jan 04 2023 23:00:52 GMT+0800 (中国标准时间)']
[1].my_map(Date)       // ['Wed Jan 04 2023 23:00:52 GMT+0800 (中国标准时间)']
[1].map(new Date())    // VM1423:1 Uncaught TypeError: [object Date] is not a function
                       // at Array.map (<anonymous>)
                       // at <anonymous>:1:5
[1].my_map(new Date()) // Uncaught TypeError: Wed Jan 04 2023 23:02:13 GMT+0800 (中国标准时间) is not a function通过类型的测试后,基本没什么太大问题,就是提示语有些出入,对我们自己实现的 my_map
稍微调整下
Array.prototype.my_map = function(fn, self){
    const type = Object.prototype.toString.call(fn)
    let newArray = []
    // 判断 fn 是否是函数 否则报错(为了跟 map 一样,报错尽量跟它一摸一样)
    let warn = ''
    
    // Object 这边提示语有点歧义🤔️
    if(type.slice(8, -1) === 'Object') 
        warn = 'TypeError: #<Object> is not a function'
    // Array
    else if(type.slice(8, -1) === 'Array')
        warn = `TypeError: ${type} is not a function`
    // Symbol
    else if(type.slice(8, -1) === 'Symbol')
        warn = `TypeError: ${fn.toString()} is not a function`
    else warn = `TypeError: ${fn} is not a function`
    
    if(typeof fn !== 'function') throw warn
    for(let i = 0; i < this.length; i++){
        newArray.push(fn.call(self, this[i], i, this))
    }
    return newArray
}
// 再一次 Object ✅
[1].map({})        // Uncaught TypeError: #<Object> is not a function
                   // at Array.map (<anonymous>)
                   // at <anonymous>:1:5
[1].my_map({})     // Uncaught TypeError: #<Object> is not a function
// 再一次 Array ✅
[1].map([])       // Uncaught TypeError: [object Array] is not a function
                  // at Array.map (<anonymous>)
                  // at <anonymous>:1:5
[1].my_map([])    // Uncaught TypeError: [object Array] is not a function
// 再一次 Symbol ✅
[1].map(Symbol('name'))     // Uncaught TypeError: Symbol(name) is not a function
                            // at Array.map (<anonymous>)
                            // at <anonymous>:1:5
[1].my_map(Symbol('name'))  // Uncaught TypeError: Symbol(name) is not a function调整完后基本可以实现 map 的效果,最后我们再来解释下 map 第二个参数具体是做什么的?🤔️
举个🌰:
const obj = {a: 2}
const fn = function(item) {
  return item * this.a
}
[1].map(fn, obj)    // [2]
[1].my_map(fn, obj) // [2]想必看到这个🌰后,应该知道当前回调函数的 this 指向了 obj,
即可以调用 obj 内的属性了 ✌️
这边你可能还有疑问,如果第二个参数不传呢? 答案:this 指向 window  
以上是个人的一些见解,如果有误,烦请帮忙指出 👉 十分感谢!
- 本文作者: MrRetro博客
- 本文链接: http://mrretro.github.io/2023/01/04/array/js数组中的map是怎么工作的/
- 版权声明: 本博客所有文章除特别声明外,均采用 MIT 许可协议。转载请注明出处!

 
                    