90%的人都不知道!揭秘es5数组方法map中的奥秘

358 阅读3分钟

1.注意细节

1.map方法支持传入除null,undefined类型以外所有类型。

2.map方法第一个参数必须为函数callback,第二个参数可选作用为callback this指向。

2.方法实现

/**
 * 
 * @param callback:回调函数
 * @param thisArg:callback this指向
 */
Array.prototype.map = function(callback, thisArg) {
	//map方法支持传入除null,undefined类型以外所有类型。
	if (this === null || this === undefined) {
	  throw new TypeError("Cannot read property 'map' of null or undefined");
	}
	//callback必须为函数
	if (Object.prototype.toString.call(callback) !== '[object Function]') {
	  throw new TypeError(callback + 'is not a function');
	}
	
	var originalValue = Object(this);
	var len = originalValue.length >>> 0;
	var newArr = new Array(len)

	for (let i = 0; i < len; i++) {
	  //如果用 hasOwnProperty 是有问题的,它只能找私有属性
	  if (i in this) {
	      var currentValue = originalValue[i];
 	      // 依次传入this, 当前项,当前索引,原数组
	      var mapResult = callbackfn.call(thisArg, currentValue, i, originalValue);
	      newArr[i] = mapResult;
	   }
         }
    return newArr;
}

3.方法细节分析

下面代码为实现最关键地方

  var originalValue = Object(this);

1.为什么originalValue = Object(this) ?

因为需要将当前类型转化为当前原始类型,否则originalValue.length和下方 in使用都将会报错。

 0 in true // Uncaught TypeError: Cannot use 'in' operator to search for '0' in true
 true.length  //undefind
 123 in true // VM90144:1 Uncaught TypeError: Cannot use 'in' operator to search for '123' in true
 123.length  // Uncaught SyntaxError: Invalid or unexpected token
 Object(123).length //undefind
 '123'.length //3
 '123' in true // VM90066:1 Uncaught TypeError: Cannot use 'in' operator to search for '123' in true   

Null,Undefined类型:map方法不支持传入这两种类型。

Object类型(Array, Function, Objcet):引用类型都具备length属性和 in 功能。

Number 类型:length属性和 in 功能都没有。

String,Boolean,Symbol类型:有length属性没有 in 功能。

小结: Object(this) 就是将所有类型转换为原始类型实例,这样才才有length属性和 in 功能。

var len = originalValue.length >>> 0;

2.为什么 var = originalValue.length >>> 0?

length >>> 0, 字面意思是指"右移 0 位",但实际上是把前面的空位用0填充,这里的作用是保证len为数字且为整数。

null >>> 0  //0

undefined >>> 0  //0

void(0) >>> 0  //0

function a (){};  a >>> 0  //0

[] >>> 0  //0

var a = {}; a >>> 0  //0

小结:将length为undefined转化为0 (undefined >>> 0 //0)

3.为什么使用 in 不使用 hasOwnProperty ?

if (i in this) {
    var currentValue = originalValue[i];
    // 依次传入this, 当前项,当前索引,原数组
    var mapResult = callbackfn.call(thisArg, currentValue, i, originalValue);
    newArr[i] = mapResult;
}

因为map支持传入对象字面量和实例,看下方例子:

//对象字面量
var obj = {
    0: 1,
    length: 100
}
Array.prototype.map.call(obj, item => item) //[1, empty × 99]
//构造函数实例
function Obj () {
    this[0] = 1;
    this.length = 100
}
Obj.prototype[1] = 2;
var obj2 = new Obj();
Array.prototype.map.call(obj, item => item) //[1, 2, empty × 98]

小结:如果使用hasOwnProperty会出现传入构造函数实例情况下无法获取构造函数实例原型链上属性。

总结

小小的map方法内部竟然有这么多细节,不得不说js博大精深。熟悉了它的原理并实现,才会发现js中的乐趣!!!

个人博客地址,有兴趣的可以看一看