【问题标题】:Understanding Array.isArray Polyfill了解 Array.isArray Polyfill
【发布时间】:2020-05-28 20:42:06
【问题描述】:

我在 MDN 上通过 Array.isArraypolyfill 方法,这是我发现的:

if (!Array.isArray) {
  Array.isArray = function(arg) {
    return Object.prototype.toString.call(arg) === '[object Array]';
  };
}

虽然这可行,但我想知道为什么 MDN 没有将以下内容列为 isArray 的 polyfill?

if (!Array.isArray) {
  Array.isArray = function(arg) {
    return arg.constructor === Array;
  };
}

上面的代码有点短和简单。我想知道使用MDN的实现与上面的相比有什么优势吗?

【问题讨论】:

  • 如果您从 iframe 中获取对象,例如,它将是 一个 数组,但不会从 您的当前 @987654328 创建@构造函数,这样检查就会失败。
  • @VLAZ:不太明白。为什么 iFrame 中的对象/数组应该有所不同?你能用代码证明你想说什么吗?
  • 它是一个数组,但来自不同的环境。构造函数是Array不同实例。正如我们所知,function() {} === function() {} 是错误的。
  • instanceof Array 与您建议的== Array 非常兼容。出现了同样的问题 - 它无法跨环境,因为您将尝试比较 Array 的不同实例。 This answer直接谈问题。

标签: javascript


【解决方案1】:

使 MDN polyfill 可以安全使用的最重要原因是数组是一个奇特的对象。区分奇异物体的最佳方法是使用与该物体相关的奇异特征——也就是说,奇异物体的奇异属性。

如果您查看Object.prototype.toString 方法的规范,您会发现它使用了抽象操作isArray,它检查奇特的数组对象。

另一方面,查看constructor property 的规范。它不是数组的奇异属性,因此 javascript 代码可以轻松更改它。

const x = [];
x.constructor = Object

事实上,constructor 属性更适合元编程。你可以——在 es5 中——创建子类而不触及构造函数属性。

现在,您的实施可能会出现以下问题:

const customIsArray = function(arg) {
    return arg.constructor === Array;
};

// 1) won't work with subclasses of Array

class CustomArray extends Array {
    // ...
}

const customArray = new CustomArray()
customIsArray(customArray) // false
Array.isArray(customArray) // true

// 2) won't work across different realms (iframes, web workers, service workers ... if we are speaking about the browser environment)

const iframe = document.createElement('iframe')
document.body.appendChild(iframe)
const IframeArray = iframe.contentWindow.Array;
const iframeArray = new IframeArray();
customIsArray(iframeArray) // false
Array.isArray(iframeArray) // true

// 3) won't work with few edge cases like (customIsArray will return true, while Array.isArray will return false)

const fakeArray1 = { __proto__: Array.prototype }
const fakeArray2 = { constructor: Array }

customIsArray(fakeArray1) // true
Array.isArray(fakeArray1) // false

customIsArray(fakeArray2) // true
Array.isArray(fakeArray2) // false 

【讨论】:

    【解决方案2】:

    一个问题是具有constructor 属性的Array 的非数组对象会在不应该通过的时候通过:

    // Bad polyfill, do not use
    Array.isArray = function(arg) {
      return arg.constructor === Array;
    };
    
    const badObj = {};
    badObj.constructor = Array;
    console.log(Array.isArray(badObj));

    上述情况会很奇怪,但 polyfill 必须尽可能地符合规范,即使它需要更复杂的代码。

    【讨论】:

    • 难道你不能也添加一个返回[object Array] 并误导MDN polyfill 的toString() 方法吗?
    • 它可能无法处理故意误导它的代码。
    • 你可以,但如果内置方法被猴子修补为不准确,那么无论如何,所有的赌注都不在桌面上。许多其他符合规范的 polyfill 可能会以这种方式被破坏。
    • @Barmar 是的,你可以。但是MDN的polyfill没有使用badObjtoString,而是使用Object.prototype.toString。我猜有人可能故意弄乱标准对象,但这是一个有点奇怪的极端情况,你可以自己防范。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-12-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-04-12
    相关资源
    最近更新 更多