JavaScript中有不同类型的数组吗?

3

这是由相同字符串组成的两个不同“数组”的控制台输出。我不知道它们是如何产生的(有很多代码我没有编写),但我认为只有Object2是真正的数组,因为它具有长度函数。调用Object1.length会返回0。

尽管在控制台中称Object1为数组,但它似乎实际上是一个带有数字属性的对象。如何确保永远不使用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(Object1) = false,而Array.isArray(Object2) = True。那么如何创建一个名为Array的对象呢?[这个问题已经在评论中由CRice回答。请参见编辑3]

编辑2: 代码在AngularJS的一个庞然大物中,但以下是一些显示数据来源的代码。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的属性,将它们推入一个新数组,然后就行了。以下是我将Object1转换为Object2的解决方案:

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

感谢CRice帮助我在控制台中重现它!


3
这真的很奇怪。请提供一个最小可重现的例子。 - Jared Smith
好的,我会尽力找到一种方法来为您复现它。该信息正在进行序列化和反序列化,因此我认为这会导致读取同一数组时出现错误。 - Zachary
1
Object1和Object2是如何被创建和填充的?能否提供相关代码?这是我们真正需要知道的。 - ADyson
3
可能的复制:o = {0: "ABC", 1: "IJK", 2: "XYZ"}; Object.setPrototypeOf(o, Array.prototype); console.log(o); 在Chrome开发工具中运行时会产生几乎相同的结果。 - CRice
有趣的问题,@CRice。我想知道添加到原型中的serialize()方法是否会使Array过载? - Zachary
最终Object1是一个空数组,在其中添加了一些属性,其中键是数字字符串。 - Mister Jojo
1个回答

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的新对象,该对象不保留真正的数组属性,而是将其转换为一个proto='Array'的对象(正如@CRice所指出的),仅此而已。要修复这个问题,我们真正需要重新设计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;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接