实现Array.isArray方法

手写实现判断一个值是否为数组的Array.isArray方法,掌握类型检测的原理

问题

Array.isArray() 是 ES5 引入的原生方法,用于判断一个值是否为数组。我们需要手动实现这个方法,能够准确判断各种情况下的数组类型,包括跨iframe的数组对象。

解答

/**
 * 实现 Array.isArray 方法
 * @param {*} value - 需要判断的值
 * @returns {boolean} - 是否为数组
 */
function myIsArray(value) {
  // 方法一:使用 Object.prototype.toString.call()
  return Object.prototype.toString.call(value) === '[object Array]';
}

// 方法二:使用 instanceof(存在跨iframe问题)
function myIsArray2(value) {
  return value instanceof Array;
}

// 方法三:使用 constructor 属性(可能被修改)
function myIsArray3(value) {
  return value && value.constructor === Array;
}

// 方法四:使用 Array.prototype.isPrototypeOf
function myIsArray4(value) {
  return Array.prototype.isPrototypeOf(value);
}

// 推荐使用方法一,最可靠
if (!Array.isArray) {
  Array.isArray = function(value) {
    return Object.prototype.toString.call(value) === '[object Array]';
  };
}

使用示例

// 基本类型测试
console.log(myIsArray([]));                    // true
console.log(myIsArray([1, 2, 3]));            // true
console.log(myIsArray(new Array()));          // true

// 非数组类型测试
console.log(myIsArray({}));                   // false
console.log(myIsArray('array'));              // false
console.log(myIsArray(123));                  // false
console.log(myIsArray(null));                 // false
console.log(myIsArray(undefined));            // false

// 类数组对象测试
console.log(myIsArray({ length: 0 }));        // false
console.log(myIsArray(arguments));            // false

// 特殊情况测试
const arrayLike = { 
  0: 'a', 
  1: 'b', 
  length: 2 
};
console.log(myIsArray(arrayLike));            // false

// TypedArray 测试
console.log(myIsArray(new Int8Array()));      // false
console.log(myIsArray(new Uint8Array()));     // false

// 跨iframe场景(方法一可以正确识别)
const iframe = document.createElement('iframe');
document.body.appendChild(iframe);
const iframeArray = window.frames[0].Array;
const arr = new iframeArray(1, 2, 3);

console.log(myIsArray(arr));                  // true
console.log(myIsArray2(arr));                 // false (instanceof失效)

关键点

  • Object.prototype.toString.call() 是最可靠的方法:返回对象的内部 [[Class]] 属性,格式为 [object Type],不会受到原型链修改的影响

  • instanceof 的局限性:在跨 iframe 场景下会失效,因为不同 iframe 有不同的 Array 构造函数

  • constructor 属性不可靠:可以被手动修改,不适合作为类型判断的依据

  • polyfill 实现:在不支持 Array.isArray 的环境中,可以通过检测并添加该方法来实现兼容

  • 与类数组对象的区别:真正的数组和类数组对象(如 arguments、NodeList)有本质区别,Array.isArray 只对真正的数组返回 true

  • TypedArray 不是 Array:虽然 TypedArray(如 Int8Array)也是数组形式,但它们不是 Array 的实例