手写实现Array.prototype.filter方法

理解并手动实现JavaScript数组的filter方法,掌握数组遍历和函数式编程的思想

问题

filter 是 JavaScript 数组的一个重要方法,用于筛选出满足条件的元素并返回一个新数组。我们需要手动实现一个功能完全相同的 myFilter 方法,理解其内部工作原理。

filter 方法接收一个回调函数作为参数,该回调函数会对数组中的每个元素进行测试,返回 true 的元素会被保留在新数组中。

解答

/**
 * 手写实现 Array.prototype.filter 方法
 * @param {Function} callback - 回调函数,接收三个参数:当前元素、索引、原数组
 * @param {*} thisArg - 可选参数,执行回调函数时的 this 值
 * @returns {Array} 返回一个新数组,包含所有通过测试的元素
 */
Array.prototype.myFilter = function(callback, thisArg) {
  // 1. 检查调用对象是否为 null 或 undefined
  if (this == null) {
    throw new TypeError('Array.prototype.myFilter called on null or undefined');
  }
  
  // 2. 检查 callback 是否为函数
  if (typeof callback !== 'function') {
    throw new TypeError(callback + ' is not a function');
  }
  
  // 3. 将调用对象转换为对象
  const O = Object(this);
  
  // 4. 获取数组长度,使用无符号右移确保为非负整数
  const len = O.length >>> 0;
  
  // 5. 创建结果数组
  const result = [];
  
  // 6. 遍历数组
  for (let i = 0; i < len; i++) {
    // 检查索引是否存在(处理稀疏数组)
    if (i in O) {
      const element = O[i];
      
      // 调用回调函数,传入当前元素、索引和原数组
      // 使用 call 绑定 thisArg
      if (callback.call(thisArg, element, i, O)) {
        result.push(element);
      }
    }
  }
  
  // 7. 返回新数组
  return result;
};

使用示例

// 示例1:筛选偶数
const numbers = [1, 2, 3, 4, 5, 6];
const evenNumbers = numbers.myFilter(num => num % 2 === 0);
console.log(evenNumbers); // [2, 4, 6]

// 示例2:筛选对象数组
const users = [
  { name: 'Alice', age: 25 },
  { name: 'Bob', age: 17 },
  { name: 'Charlie', age: 30 }
];
const adults = users.myFilter(user => user.age >= 18);
console.log(adults); 
// [{ name: 'Alice', age: 25 }, { name: 'Charlie', age: 30 }]

// 示例3:使用 thisArg 参数
const filterConfig = {
  minValue: 10
};
const values = [5, 15, 8, 20, 12];
const filtered = values.myFilter(function(num) {
  return num >= this.minValue;
}, filterConfig);
console.log(filtered); // [15, 20, 12]

// 示例4:处理稀疏数组
const sparseArray = [1, , 3, , 5];
const result = sparseArray.myFilter(num => num > 2);
console.log(result); // [3, 5]

// 示例5:获取索引信息
const arr = ['a', 'b', 'c', 'd'];
const withIndex = arr.myFilter((item, index) => index % 2 === 0);
console.log(withIndex); // ['a', 'c']

关键点

  • 参数校验:需要检查 this 是否为 null/undefined,以及 callback 是否为函数类型
  • thisArg 绑定:使用 call 方法将 thisArg 绑定到回调函数的执行上下文
  • 稀疏数组处理:使用 in 操作符检查索引是否存在,跳过空位元素
  • 不修改原数组:创建新数组存储结果,保持原数组不变
  • 回调函数参数:正确传递三个参数:当前元素值、当前索引、原数组对象
  • 长度处理:使用 >>> 0 无符号右移运算确保长度为非负整数
  • 返回新数组:filter 方法总是返回一个新数组,即使没有元素通过测试也返回空数组