Polyfill 数组的 map 方法

378 阅读3分钟

先贴上MDN上对map()的解释:map() 方法创建一个新数组,其结果是该数组中的每个元素是调用一次提供的函数后的返回值。

语法

var new_array = arr.map(function callback(currentValue[, index[, array]]) {
 // Return element for new_array 
}[, thisArg])

参数

  • callback

    生成新数组元素的函数,使用三个参数:

    • currentValue

      callback 数组中正在处理的当前元素。

    • index可选

      callback 数组中正在处理的当前元素的索引。

    • array可选

      map 方法调用的数组。

  • thisArg可选

    可选参数。当执行回调函数 callback 时,如果 thisArg 参数有值,则每次 callback 函数被调用时,this 都会指向 thisArg 参数。如果省略了 thisArg 参数,或者其值为 null 或 undefinedthis 则指向全局对象

返回值

一个由原数组每个元素执行回调函数的结果组成的新数组。

边界条件

  1. thisArg 有值,需要处理 callbakcthis 指向
  2. 根据规范中定义的算法,如果被map调用的数组是离散的,新数组将也是离散的保持相同的索引为空。
  3. callback 中改变了原数组

map 方法会给原数组中的每个元素都按顺序调用一次  callback 函数。callback 每次执行后的返回值(包括 undefined)组合起来形成一个新数组。 callback 函数只会在有值的索引上被调用;那些从来没被赋过值或者使用 delete 删除的索引则不会被调用。

map 方法处理数组元素的范围是在 callback 方法第一次调用之前就已经确定了。调用map方法之后追加的数组元素不会被callback访问。如果存在的数组元素改变了,那么传给callback的值是map访问该元素时的值。在map函数调用后但在访问该元素前,该元素被删除的话,则无法被访问到

返回值

一个由原数组每个元素执行回调函数的结果组成的新数组。

Polyfill

/**
 *
 * @param callback
 * @param thisArg
 * @returns {*[]}
 */
Array.prototype.map = function(callback, thisArg) {
  if (!Array.isArray(this)) {
    throw new TypeError("this is not a array");
  }
  if (typeof callback !== "function") {
    throw new TypeError(`${callback} is not a function`);
  }
  const result = [];
  const len = this.length;
  let index = 0;
  while (index < len) {
    if (index in this) {
      result[index] = callback.call(thisArg, this[index], index, this);
    }
  index++
  }
  return result;
};

MDN Polyfill

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 = arguments[1];
 }

 // 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;
};