实现数组方法 map、filter、reduce、find、some、every

手写 JavaScript 数组常用方法的实现

问题

从零实现数组的 mapfilterreducefindsomeevery 方法。

解答

实现 map

Array.prototype.myMap = function(callback, thisArg) {
  // 边界检查
  if (typeof callback !== 'function') {
    throw new TypeError(callback + ' is not a function');
  }
  
  const result = [];
  for (let i = 0; i < this.length; i++) {
    // 跳过稀疏数组的空位
    if (i in this) {
      // callback 接收三个参数:当前元素、索引、原数组
      result.push(callback.call(thisArg, this[i], i, this));
    }
  }
  return result;
};

// 测试
const arr = [1, 2, 3];
console.log(arr.myMap(x => x * 2)); // [2, 4, 6]

实现 filter

Array.prototype.myFilter = function(callback, thisArg) {
  if (typeof callback !== 'function') {
    throw new TypeError(callback + ' is not a function');
  }
  
  const result = [];
  for (let i = 0; i < this.length; i++) {
    if (i in this) {
      // 只有 callback 返回 true 时才加入结果
      if (callback.call(thisArg, this[i], i, this)) {
        result.push(this[i]);
      }
    }
  }
  return result;
};

// 测试
const arr = [1, 2, 3, 4, 5];
console.log(arr.myFilter(x => x > 2)); // [3, 4, 5]

实现 reduce

Array.prototype.myReduce = function(callback, initialValue) {
  if (typeof callback !== 'function') {
    throw new TypeError(callback + ' is not a function');
  }
  
  const len = this.length;
  let accumulator;
  let startIndex = 0;
  
  // 处理初始值
  if (arguments.length >= 2) {
    accumulator = initialValue;
  } else {
    // 没有初始值时,找到第一个有效元素作为初始值
    if (len === 0) {
      throw new TypeError('Reduce of empty array with no initial value');
    }
    // 跳过稀疏数组的空位
    while (startIndex < len && !(startIndex in this)) {
      startIndex++;
    }
    if (startIndex >= len) {
      throw new TypeError('Reduce of empty array with no initial value');
    }
    accumulator = this[startIndex++];
  }
  
  for (let i = startIndex; i < len; i++) {
    if (i in this) {
      // callback 接收四个参数:累加器、当前值、索引、原数组
      accumulator = callback(accumulator, this[i], i, this);
    }
  }
  
  return accumulator;
};

// 测试
const arr = [1, 2, 3, 4];
console.log(arr.myReduce((acc, cur) => acc + cur, 0)); // 10
console.log(arr.myReduce((acc, cur) => acc + cur));    // 10

实现 find

Array.prototype.myFind = function(callback, thisArg) {
  if (typeof callback !== 'function') {
    throw new TypeError(callback + ' is not a function');
  }
  
  for (let i = 0; i < this.length; i++) {
    if (i in this) {
      // 找到第一个满足条件的元素就返回
      if (callback.call(thisArg, this[i], i, this)) {
        return this[i];
      }
    }
  }
  // 没找到返回 undefined
  return undefined;
};

// 测试
const arr = [1, 2, 3, 4];
console.log(arr.myFind(x => x > 2)); // 3
console.log(arr.myFind(x => x > 10)); // undefined

实现 some

Array.prototype.mySome = function(callback, thisArg) {
  if (typeof callback !== 'function') {
    throw new TypeError(callback + ' is not a function');
  }
  
  for (let i = 0; i < this.length; i++) {
    if (i in this) {
      // 只要有一个满足条件就返回 true
      if (callback.call(thisArg, this[i], i, this)) {
        return true;
      }
    }
  }
  // 空数组返回 false
  return false;
};

// 测试
const arr = [1, 2, 3, 4];
console.log(arr.mySome(x => x > 3)); // true
console.log(arr.mySome(x => x > 10)); // false
console.log([].mySome(x => x > 0)); // false

实现 every

Array.prototype.myEvery = function(callback, thisArg) {
  if (typeof callback !== 'function') {
    throw new TypeError(callback + ' is not a function');
  }
  
  for (let i = 0; i < this.length; i++) {
    if (i in this) {
      // 只要有一个不满足条件就返回 false
      if (!callback.call(thisArg, this[i], i, this)) {
        return false;
      }
    }
  }
  // 空数组返回 true
  return true;
};

// 测试
const arr = [1, 2, 3, 4];
console.log(arr.myEvery(x => x > 0)); // true
console.log(arr.myEvery(x => x > 2)); // false
console.log([].myEvery(x => x > 0)); // true

关键点

  • 使用 i in this 判断稀疏数组的空位,避免处理不存在的元素
  • callback 通过 call 绑定 thisArg,支持指定执行上下文
  • reduce 无初始值时,用第一个有效元素作为初始值,空数组要抛错
  • some 空数组返回 falseevery 空数组返回 true(逻辑学约定)
  • 所有方法都要做类型检查,callback 不是函数时抛出 TypeError