Существуют ли различные типы массивов в JavaScript?

#javascript #arrays #javascript-objects #conventions

Вопрос:

Это консольный вывод двух разных «массивов», состоящих из одних и тех же строк. Я не знаю, как они возникли (есть много кода, который я не писал), но я считаю, что только Object2 является истинным массивом, поскольку он имеет функцию длины. Вызов Object1.длина дает 0.

Несмотря на то, что объект 1 в консоли называется массивом, на самом деле он выглядит как объект с числовыми атрибутами. Как я могу гарантировать, что массив типа Object1 никогда не используется и все массивы выглядят как Object2?

 Object1
Array {0: 'ABC', 1: 'IJK', 2: 'XYZ'}
0: "ABC"
1: "IJK"
2: "XYZ"
[[Prototype]]: Array(0)

Object2
(3) ['ABC', 'IJK', 'XYZ']
0: "ABC"
1: "IJK"
2: "XYZ"
length: 3
 

Правка 1:
Object1 и Object2-это просто имена переменных, которые я использовал для этого примера.

Вызов Array.isArray(Объект 1) = false, в то время как Array.isArray(Объект 2) = True. Как бы вообще можно было создать объект, называемый массивом? [На это ответил КРайс в комментариях. См. Правку 3]

Правка 2: Код находится в чудовищном угловом положении, но вот некоторый код, показывающий, как возникают данные. Object1-это переменная, содержащая то, что должно быть массивом под названием «someArray» в приведенной ниже модели, и она инициализируется с помощью адаптера. Object2 был сделан как традиционный встроенный массив: Object2 = [];

Модель

 define([], function () {
  return [
    function () {
      function SomeObjectModel(someArray) {
        this.someArray = someArray;
      }

      SomeObjectModel.prototype.serialize = function () {
        return {
          someArray: this.someArray,
        };
      };

      SomeObjectModel.build = function (data) {
        return new SomeObjectModel(data.someArray);
      };

      return SomeObjectModel;
    },
  ];
});
 

Адаптер

 var serializedSomeObject = someObjectInstance.serialize();
return $http({
  method: 'PUT',
  url: 'someUrl.com/api',
  data: serializedSomeObject,
}).then(function (response) {
  return SomeObjectModel.build(response.data);
});
 

Изменить 3:
@CRice указал, что этот код может воспроизвести экземпляр Object1, который называет себя массивом, но не ведет себя как таковой:

 var o = { 0: 'ABC', 1: 'IJK', 2: 'XYZ' };
Object.setPrototypeOf(o, Array.prototype);
console.log(o);
 

Изменить 4:
Я не знаю, почему сериализация, а затем десериализация вызвали это, но мое решение состоит в том, чтобы повторить атрибуты Object1, просто поместить их в новый массив и закрыть его. Вот мое решение для преобразования объекта 1 в объект 2:

 var Object2 = [];
var attr = Object.getOwnPropertyNames(Object1);
for(var i=0; i<attr.length; i  ){
  Object2.push(Object1[attr[i]]);
}
console.log(Object2);
 

Спасибо Крайсу за то, что помог мне воспроизвести его в консоли!

Комментарии:

1. Это … действительно странно. Пожалуйста, опубликуйте минимальный воспроизводимый пример

2. Хорошо, я сделаю все возможное, чтобы найти способ воспроизвести его для вас. Информация сериализуется, а затем десериализуется, поэтому я думаю, что это приводит к неправильному чтению одного и того же массива.

3. Как именно были созданы и заселены объекты 1 и 2? Это то, что нам действительно нужно знать. Покажите этот код, пожалуйста.

4. Возможное воспроизведение: o = {0: "ABC", 1: "IJK", 2: "XYZ"}; Object.setPrototypeOf(o, Array.prototype); console.log(o); дает почти идентичный результат при запуске в Chrome devtools.

5. Интересно, @CRice. Интересно, был ли метод serialize (), добавленный в прототип, перегружен массивом?

Ответ №1:

Я нашел причину. Существует модуль, который глубоко клонирует сложные объекты, чтобы их можно было восстановить в будущем. Можете ли вы определить ошибку в этом рекурсивном цикле?

 function cloneObject(src) {
    let target = {};
    target.__proto__ = src.__proto__;
    for (let prop in src) {
        if (src.hasOwnProperty(prop)) {
            // if the value is a nested object, recursively copy all it's properties
            if (isObject(src[prop])) {
                target[prop] = cloneObject(src[prop]);
            } else {
                target[prop] = src[prop];
            }
        }
    }
    return target;
}
 

Когда someArray проходит через функцию cloneObject (), он создает новый объект под названием Array, который не сохраняет истинные свойства массива, а вместо этого преобразует его в объект с прото= «Массив» (как указал @КРайс) и ничего больше. Чтобы исправить это, нам действительно нужно переработать функцию cloneObject для сохранения массивов. Это исправленная функция глубокого копирования cloneObject, которую я сейчас использую:

 function cloneObject(src) {
    let target = {};
    var isArray = Array.isArray(src);
    if(isArray){
        target = [];
    } else {
        target.__proto__ = src.__proto__;
    }

    for (let prop in src) {
        if (src.hasOwnProperty(prop)) {
            // if the value is a nested object, recursively copy all it's properties
            if (isObject(src[prop])) {
                var propertyValue = cloneObject(src[prop]);
            } else {
                var propertyValue = src[prop];
            }
            
            // if src was an array
            if(isArray){
                target.splice(prop, 0, propertyValue);
            } else {
                target[prop] = propertyValue;
            }
        }
    }
    return target;
}