提问者:小点点

是否可以在不使用数组构造函数或数组文字的情况下创建一个Array.isArray()返回true的对象?


我可以通过将普通对象的原型设置为array.prototype:

const obj = {};
Reflect.setPrototypeOf(obj, Array.prototype);

(我知道神奇的length属性和稀疏数组也存在一些问题,但这不是这个问题的重点。)

我想使array.isArray(obj)返回true(当然不修改array.isArray()方法)。 Array.IsArray()的MDN polyfill如下所示:

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

通过使用Symbol.ToStringTag属性,我可以使object.Prototype.ToString.Call(obj)返回“[object Array]”:

obj[Symbol.toStringTag] = 'Array';
console.log(Object.prototype.toString.call(obj) === '[object Array]'); // true

现在,多填充的array.isArray()obj返回true(请忽略一个事实,即不支持array.isArray()的浏览器都不支持symbol.ToStringTag)。 但是,本机array.isArray()函数仍然为obj返回false。 我查看了ECMAScript 2017规范,它说Array.isArray()使用抽象操作isArray,如果参数是数组外来对象,则返回true。 如果参数是一个代理,它将直接调用目标对象上的IsArray,因此在这里使用代理似乎没有帮助。

有没有办法使array.isArray(obj)返回true? 为了明确,我不想修改array.isArray()或任何其他内置对象。

这与您可以用用户定义的对象伪装Array.isArray()基本相同,但这个问题是5年前提出的,答案是基于ECMAScript5规范的。 我正在寻找一个基于ECMAScript 2017规范的答案。


共3个答案

匿名用户

不,正如您已经说过的,一个真正的数组(也就是array.isArray检测到的)是一个数组外来对象,这意味着它的.length以一种特殊的方式运行。

唯一的构造方法是使用数组构造函数或array的子类(它反过来调用数组构造函数),或者从另一个领域使用相同的方法。 还有无数其他方法返回新数组(例如String::SplitString::MatchArray.FromArray.ofArray.ofArray原型方法,Object.KeyObject.GetOwnPropertyNames)。
此外,用于标记模板或作为代理应用/构造陷阱的函数将接收全新的数组,并且数组也被构造为

如果您正在寻找创建数组的语法方法,那么array literal将是您的主要选择,但是析构表达式(在array literal或函数中)也可以从迭代器创建数组。

如果您的实际问题是“我能把一个任意对象变成一个数组奇异对象吗?”,答案是肯定的“否”。

匿名用户

一个对象被创建为一个奇异数组或者不是,你不能改变它。

但是,正如您所提到的,您可以创建一个目标是数组的代理对象。

null

console.log(Array.isArray(new Proxy([], {}))) // true

匿名用户

Babel可以将类MyObject extends Array转换为ES5代码,从而使Array.IsArray(new MyObject)返回true:现场演示

class MyObject extends Array{};

console.log(Array.isArray(new MyObject))
"use strict";

function _possibleConstructorReturn(self, call) {
    if (!self) {
        throw new ReferenceError(
            "this hasn't been initialised - super() hasn't been called"
        );
    }
    return call && (typeof call === "object" || typeof call === "function")
        ? call
        : self;
}

function _inherits(subClass, superClass) {
    if (typeof superClass !== "function" && superClass !== null) {
        throw new TypeError(
            "Super expression must either be null or a function, not " +
                typeof superClass
        );
    }
    subClass.prototype = Object.create(superClass && superClass.prototype, {
        constructor: {
            value: subClass,
            enumerable: false,
            writable: true,
            configurable: true
        }
    });
    if (superClass)
        Object.setPrototypeOf
            ? Object.setPrototypeOf(subClass, superClass)
            : (subClass.__proto__ = superClass);
}

var MyObject = (function(_Array) {
    _inherits(MyObject, _Array);

    function MyObject() {
        return _possibleConstructorReturn(
            this,
            (MyObject.__proto__ || Object.getPrototypeOf(MyObject))
                .apply(this, arguments)
        );
    }

    return MyObject;
})(Array);

console.log(Array.isArray(new MyObject()));