【问题标题】:Is there any way to turn an existing Javascript object into an array without creating a new separate array?有没有办法在不创建新的单独数组的情况下将现有的 Javascript 对象转换为数组?
【发布时间】:2019-06-28 20:42:36
【问题描述】:

在不同的阅读中有很多提到数组是 Javascript 中的一类特殊对象。例如这里:

https://www.codingame.com/playgrounds/6181/javascript-arrays---tips-tricks-and-examples

所以,由于 object 是属性(或键)和值的集合,我在想是否有一种方法可以从一个对象开始并以一个数组结束(在某种意义上,@987654327 方法@ 为模拟数组的对象返回 true)。我已经开始查看数组属性:

let arr = [0, 1, 2, 3, 4, 5];
console.log(Object.getOwnPropertyNames(arr));
console.log(Array.isArray(arr));

所以我尝试使用一个对象来模拟:

let arrEmulation = {0:0, 1:1, 2:2, 3:3, 4:4, 5:5, "length":6};
console.log(Object.getOwnPropertyNames(arrEmulation));
console.log(Array.isArray(arrEmulation));

但是Array.isArray(arrEmulation) 仍然返回false。首先,如果这是一个愚蠢的问题,我想道歉,但有什么方法可以手动将 object 转换为 array 添加特殊属性(或键)

请注意,我不想知道如何将对象转换为数组,我只想了解哪些特殊事物可以像数组一样解释对象。 p>

【问题讨论】:

标签: javascript arrays object


【解决方案1】:

您可以使用 Array.from() 将任何足够接近数组的内容转换为数组。 在您的示例中,我们可以调用:

const arrEmulation = {0:0, 1:1, 2:2, 3:3, 4:4, 5:5, length: 6};

const newArray = Array.from(arrEmulation) // [0, 1, 2, 3, 4, 5]
Array.isArray(newArray) // true

【讨论】:

  • 我不是在看如何将对象转换为数组,而是要了解它们之间的差异......
  • @SmaGal,说句公道话 - 你不是在问题标题中说“convert”吗?
  • @eric99 否决票不是来自我。我只是解释说我不是在寻找那个。
  • @SmaGal 我的评论有点半开玩笑。 TioZed 的回答值得称赞,因为这个问题现在已经演变成更具体的问题。
【解决方案2】:

从最严格的意义上说,鉴于标准规范,我认为这是不可能的。抬头Array.isArray

如果arg的[[Class]]内部属性的值为“Array”,则返回true。

因此,要使Array.isArray(arrEmulation) 返回true,您必须以某种方式将对象的[[Class]] 修改为Array,而不是Object。但是,看看 ES5 的 8.6.2 Object Internal Properties and Methods 关于 [[Class]]

注意:本规范没有定义允许程序修改对象的 [[Class]] 或 [[Prototype]] 内部属性或更改 [[Extensible]] 的值的 ECMAScript 语言运算符或内置函数假对真。修改 [[Class]]、[[Prototype]] 或 [[Extensible]] 的实现特定扩展不得违反前一段中定义的不变量。

还有:

请注意,除了通过Object.prototype.toString 之外,本规范没有为程序提供任何访问该值的方法

因此,官方规范并没有提供在 ES5 中执行此操作的方法 - 如果有 执行此操作的方法,那么它将是非标准的并且依赖于实现。

也就是说,除非你绝对需要使用Array.isArray或让Object.prototype.toString.call(arrEmulation)返回[object Array],否则你仍然可以使用Object.setPrototypeOfarrEmulation的原型设置为@ 987654337@,允许您在对象上使用数组方法并让instanceof Array返回true

const arrEmulation = {0:0, 1:1, 2:2, "length":6};
Object.setPrototypeOf(arrEmulation, Array.prototype);

console.log(arrEmulation instanceof Array);
arrEmulation.forEach((value) => {
  console.log(value);
});
// Internal [[Class]] property is still `Object`, though:
console.log(Object.prototype.toString.call(arrEmulation));
// Unlike a true array:
console.log(Object.prototype.toString.call([]));

console.log('-----');

// although you can set the `toStringTag` to the string 'Array' in ES6+,
// it is cosmetic only and does not pass an `Array.isArray` test:
arrEmulation[Symbol.toStringTag] = 'Array';
console.log(Object.prototype.toString.call(arrEmulation));
console.log(Array.isArray(arrEmulation));

但请注意,您应该在实际代码中使用avoid Object.setPrototypeOf

警告:根据现代 JavaScript 引擎优化属性访问的本质,在每个浏览器和 JavaScript 引擎中更改对象的 [[Prototype]] 是一项非常缓慢的操作。更改继承对性能的影响是微妙而广泛的,并且不仅限于在Object.setPrototypeOf(...) 语句中花费的时间,还可能扩展到可以访问任何[[Prototype]] 已更改的对象的任何代码。如果您关心性能,则应避免设置对象的[[Prototype]]。相反,使用Object.create() 创建一个具有所需[[Prototype]] 的新对象。

(当然Object.create涉及创建一个new对象,这和你想做的不同,就是改变现有的arrEmulation对象)

在 ES6+ 中似乎也没有办法做到这一点 - 它的文本有点相似,但不完全相同。具体来说,对于Array.isArray 返回true,有问题的对象needs to be 是一个“数组外来对象”(或指向一个的Proxy) - 但setPrototypeOf 只设置原型,既不是它也不是任何其他方法可以使对象实际上变成一个 Array 外来对象(看起来它必须由解释器本地构造,并且不够可模拟)。

【讨论】:

  • 谢谢老兄,这些信息让我更清楚地了解标准对象和数组之间的区别。就你所知,我并不是真的想用一个对象来模拟一个数组,而只是为了了解它们的差异,这对我有帮助。 +1!
  • @SmaGal 典型 JavaScript 引擎中的实现将内置进一步优化。典型的数组很可能是内存中的密集块,可通过指针直接访问。对于相当静态的对象也是如此,其属性将被对内存结构的内存地址的访问所取代。在这两种情况下,运行时密集型方法都将被编译为通过直接指针快速访问内存。
【解决方案3】:

Javascript 都是关于原型继承的:

Prototype Inheritance 所有 JavaScript 对象都从一个 原型:

日期对象继承自 Date.prototype 数组对象继承自 Array.prototype Person 对象继承自 Person.prototype 的 Object.prototype 位于原型继承链的顶端:

Date 对象、Array 对象和 Person 对象继承自 Object.prototype。

从这里可以看出,isArray 是 Array 对象原型链中的一个函数。

如果 isArray 不存在,MDN Array.isArray() 中建议的 polyfill 是:

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

所以类型是由原型链决定的,而不是它返回的值。

同样,根据 Tio Zed 的回答

const newArray = Array.from(arrEmulation) // [0, 1, 2, 3, 4, 5]
Array.isArray(newArray)

它真正做的只是将原型从对象的原型更改为数组的原型。

更深入地了解 isArray 感谢@Kaiido 让我更深入地挖掘。 数组是数组检查these there points

如果 Type(arg) 不是 Object,则返回 false。 如果 arg 的 [[Class]] 内部属性的值为“Array”, 然后返回真。返回 false。

And

Array 实例从 Array 原型对象继承属性,并且 它们的 [[Class]] 内部属性值为“Array”。数组实例 还具有以下属性。

【讨论】:

  • 哼...不。这不是Array.isArray 检查的内容。这是一篇发布在 MDN 文章上的简单 polyfill,任何浏览器都不会实现它。这是what the specs asks,尽管他们不清楚如何检查它是否是一个数组外来对象。这是一个证明它不是实现方式的证据:jsfiddle.net/f6182mkh
  • 我编辑了我的答案,我只是想表明这一切都取决于原型,而不是他们在值中看到的键。顺便说一句,感谢您纠正我。如果您仍然觉得我的回答有问题,请编辑我的回答。 @凯多
  • 好吧,即使原型也不正确:jsfiddle.net/vLrgdp86
  • 上面我定义的目前在很多不支持的浏览器中作为Polyfill使用。我已经搜索了它目前是如何正式实施的,但没有成功。如果您可以为我提供一份文档,将会很有帮助。这里有一些关于我能找到的Array.isArray 的细节。 A official doc 在这里展示了如何实现原型来定义数组 @Kaiido
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-04-04
  • 2019-08-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多