Есть ли способ превратить существующий объект Javascript в массив, не создавая новый отдельный массив? - PullRequest
0 голосов
/ 05 февраля 2019

Есть много упоминаний о различных показаниях, что массивы - это особый класс объектов в Javascript.Например, здесь:

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

Итак, так как object - это набор свойств (или ключей) и значений, я подумал, есть ли способ начать собъект и заканчивается массивом (в том смысле, что метод Array.isArray() возвращает 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, добавив специальные свойства (или ключи) к нему?

Пожалуйста, обратите внимание, я не хочу знать, как преобразовать объект в массив, я просто хочу понять, какие это специальные вещи , которые позволяют интерпретировать объект как массив.

Ответы [ 3 ]

0 голосов
/ 05 февраля 2019

Я не думаю, что это возможно, в самом строгом смысле, учитывая стандартную спецификацию.Поиск Array.isArray :

Если значение внутреннего свойства [[Class]] для arg равно "Array", тогда вернуть true.

Таким образом, чтобы Array.isArray(arrEmulation) вернул true, вы должны как-то изменить [[Class]] объекта, чтобы оно было Array, а не Object.Но, глядя на ES5 8.6.2. Внутренние свойства и методы объекта относительно [[Class]]:

Примечание. Эта спецификация не определяет операторов языка ECMAScript или встроенных функций, которые разрешаютпрограмма для изменения внутренних свойств объекта [[Class]] или [[Prototype]] или для изменения значения [[Extensible]] с false на true.Специфичные для реализации расширения, которые изменяют [[Class]], [[Prototype]] или [[Extensible]], не должны нарушать инварианты, определенные в предыдущем абзаце.

Также:

Обратите внимание, что эта спецификация не предоставляет программам никаких средств для доступа к этому значению, кроме как через Object.prototype.toString

Итак, официальная спецификация не предоставляет способ сделать это в ES5 -если бы был способ сделать это, он был бы нестандартным и зависящим от реализации.

Тем не менее, если вам абсолютно не нужно для использования Array.isArray илиесть Object.prototype.toString.call(arrEmulation) для возврата [object Array], вы все равно можете использовать Object.setPrototypeOf для установки прототипа arrEmulation на Array.prototype, что позволяет вам использовать методы массива для объекта и иметь instanceof Array return 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));

Но учтите, что вам следует избегать , используя Object.setPrototypeOf в реальном коде:

Предупреждение: изменение [[Prototype]] объект по природе того, как современные движки JavaScript оптимизируют доступ к свойствам, является очень медленной операцией в каждом браузере и движке JavaScript.Влияние изменения производительности наследования на тонкость и масштабность не ограничивается простым временем, затрачиваемым в операторе Object.setPrototypeOf(...), но может распространяться на любой код, который имеет доступ к любому объекту, чье [[Prototype]] было изменено.Если вы заботитесь о производительности, вам следует избегать установки [[Prototype]] объекта.Вместо этого создайте новый объект с желаемым [[Prototype]], используя Object.create().

(конечно, Object.create предполагает создание нового объекта, который отличается от того, чтовы хотите сделать, то есть изменить существующий объект arrEmulation)

В ES6 + также не существует способа сделать это - его текст чем-то похож ,но не идентичны.В частности, чтобы Array.isArray возвратил true, рассматриваемый объект должен быть «Массивом экзотического объекта» (или Proxy, который указывает на единицу), но setPrototypeOf устанавливает толькоПрототип, ни он, ни любой другой метод не могут сделать объект фактически превращенным в экзотический объект Array (который выглядит так, как будто он должен быть изначально создан интерпретатором, и не является достаточно эмулируемым).

0 голосов
/ 05 февраля 2019

Javascript - это все о наследовании прототипа:

Наследование прототипа Все объекты JavaScript наследуют свойства и методы от прототипа:

Объекты Date наследуются от Date.Прототипные объекты Array наследуются от Array.prototype. Объекты Person наследуются от Person.prototype. Object.prototype находится в верхней части цепочки наследования прототипа:

Объекты Date, объекты Array и объекты Person наследуются от Object.prototype.

Как видно, isArray - это функция в цепочке прототипов объекта Array.

Полифилл, предложенный в MDN Array.isArray () , альтернативный, если isArrayотсутствует:

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

Таким образом, тип определяется цепочкой прототипов, а не значением, которое он возвращает.

Аналогично, согласно ответу Тио Зеда

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

Что он на самом деле делает, так это просто изменяет прототип с объекта на массив.

Более глубокий анализ isArРэй, спасибо, @Kaiido, что заставил меня копать глубже.Массив является проверкой массива этих точек

Если Type (arg) не является Object, вернуть false.Если значение внутреннего свойства [[Class]] для arg равно «Array», верните true.Вернуть false.

И

Экземпляры Array наследуют свойства от объекта-прототипа Array, а их внутреннее свойство [[Class]] имеет значение "Array"».Экземпляры массива также имеют следующие свойства.

0 голосов
/ 05 февраля 2019

Вы можете преобразовать все, что достаточно близко к массиву, используя 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...