3

This is the console output of two different "arrays" made up of the same strings. I do not know how they originated (there is lots of code that I did not write) but I believe only Object2 is a true Array as it has a length function. Calling Object1.length gives 0.

Even though Object1 is called an Array in the console, it appears to actually be an Object with numerical attributes. How do I ensure the Object1 type of array is never used and all arrays look like 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

Edit 1: Object1 and Object2 are just variable names I used for this example.

Calling Array.isArray(Object1) = false while Array.isArray(Object2) = True. How would one even make an Object called Array? [This was answered by CRice in the comments. See Edit 3]

Edit 2: The code is in an AngularJS monstrosity, but here is some code showing how the data is originated. Object1 is a variable holding what should be an array called "someArray" in the model below and it is initialized using the adapter. Object2 was made like a traditional inline array: Object2 = [];

Model

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;
    },
  ];
});

Adapter

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

Edit 3: @CRice pointed out that this code can reproduce the Object1 instance which calls itself Array but does not behave as such:

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

Edit 4: I don't know why serializing and then deserializing caused this but my solution is to iterate over the attributes of Object1 and just push them into a new array and call it a day. Here is my solution to convert Object1 to Object2:

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

Thank you CRice for helping me reproduce it in the console!

6
  • 3
    This is....really weird. Please post a minimum reproduceable example Commented Sep 23, 2021 at 22:09
  • Ok, I'll do my best to find a way to reproduce it for you. The information is getting serialized and then deserialized so I think that is causing the same array to be read incorrectly. Commented Sep 23, 2021 at 22:14
  • 1
    How did Object1 and Object2 get created and populated, exactly? That's what we really need to know. Show that code please. Commented Sep 23, 2021 at 22:21
  • 3
    Possible reproduction: o = {0: "ABC", 1: "IJK", 2: "XYZ"}; Object.setPrototypeOf(o, Array.prototype); console.log(o); produces a nearly identical result when ran in the Chrome devtools. Commented Sep 23, 2021 at 22:32
  • Interesting, @CRice. I wonder if the serialize() method that was added to the prototype overloaded Array? Commented Sep 23, 2021 at 22:40

1 Answer 1

1

I found the cause. There is a module that deep clones complex objects so they can be restored in the future. Can you spot the error in this recursive loop?

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;
}

When someArray goes through the cloneObject() function, it creates a new object called Array that does not retain true array properties but instead converts it into an object with proto='Array' (as @CRice pointed out) and nothing more. To fix this we really need to rework the cloneObject function to preserve arrays. This is the corrected deep copy cloneObject function I am now using:

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;
}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.