通过将原型设置为Array.prototype
:
const obj = {};
Reflect.setPrototypeOf(obj, Array.prototype);
(我知道魔术length
属性和稀疏数组也存在一些问题,但这不是这个问题的重点。)
我想让Array.isArray(obj)
返回true
(当然不会修改Array.isArray()
方法)。 MDN polyfill for Array.isArray()
如下:
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()
会为true
返回obj
(请忽略不支持Array.isArray()
的所有浏览器都支持Symbol.toStringTag
的事实}})。但是,原始Array.isArray()
函数仍会为false
返回obj
。我查看了ECMAScript 2017规范,它说Array.isArray()
使用抽象操作IsArray
,如果参数是Array外来对象,则返回true
。如果参数是一个代理,它会直接在目标对象上调用IsArray
,所以似乎使用代理在这里没有帮助。
有没有办法让Array.isArray(obj)
返回true
?为了说清楚,我不想修改Array.isArray()
或任何其他内置对象。
这与Can you fake out Array.isArray() with a user-defined object?基本上是同一个问题,但5年前被问过,答案基于ECMAScript 5规范。我正在寻找基于ECMAScript 2017规范的答案。
答案 0 :(得分:6)
不,正如您已经说过的一个真正的数组(Array.isArray
检测到的)是一个数组异域对象,这意味着它的.length
以特殊方式运行
构造它的唯一方法是使用数组构造函数或Array
的子类(进而调用数组构造函数)或来自另一个领域的相同方法。此外,无数其他方法返回新数组(例如String::split
,String::match
,Array.from
,Array.of
,Array
原型方法,Object.keys
,{{ 1}})。
此外,用于标记模板或代理应用/构造陷阱的函数将接收全新的数组,并且数组也构造为Object.getOwnPropertyNames
或Promise.all
迭代器的结果的一部分。
如果您正在寻找创建数组的语法方法,那么数组文字将是您的主要选择,但是解构表达式(在数组文字或函数中)也可以从迭代器创建数组。
如果您的实际问题是" 我可以将任意对象转换为数组奇异对象吗?",答案是坚定的否。
答案 1 :(得分:1)
对象要么创建为异形数组,要么不能创建。
但是,正如您所提到的,您可以创建一个目标为数组的Proxy对象。
console.log(Array.isArray(new Proxy([], {}))) // true

Proxy对象不会是一个数组,但会被Array.isArray
视为一个数组。它将是一个新的不同对象,但您可以将所有内部操作重定向到所需对象,从而有效地成为实时克隆。
function redirect(trap) {
return (target, ...args) => Reflect[trap](obj, ...args);
}
var obj = {0:0, 1:1, length:2};
var arrayified = new Proxy([], {
apply: redirect('apply'),
construct: redirect('construct'),
defineProperty: redirect('defineProperty'),
deleteProperty: redirect('deleteProperty'),
enumerate: redirect('enumerate'),
get: redirect('get'),
getOwnPropertyDescriptor: redirect('getOwnPropertyDescriptor'),
getPrototypeOf: redirect('getPrototypeOf'),
has: redirect('has'),
isExtensible: redirect('isExtensible'),
ownKeys: redirect('ownKeys'),
preventExtensions: redirect('preventExtensions'),
set: redirect('set'),
setPrototypeOf: redirect('setPrototypeOf')
});
console.log(arrayified); // [0, 1]

答案 2 :(得分:0)
不,你不能。您可以非常轻松地创建类似对象的数组,但与.push()
或其他自行修改length
属性的数组方法不同,当您这样做时,您永远不能使length
属性自行增加{ {1}}在异常数组中,如a[a.length] = "test"
或nodeList
。
This就是我可以创建一个Frankenstarray,而不使用正确的数组来扩展,而是使用适当的对象。
答案 3 :(得分:0)
Babel可以将class MyObject extends Array
转换为导致Array.isArray(new MyObject)
返回true
的ES5代码:
Live demo
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()));