Javascript数组变成对象结构

14

我遇到了一个奇怪的行为(也许并不奇怪,只是我不理解为什么)涉及一个包含一些对象的 JavaScript 数组。

由于我不是 JavaScript 专家,可能有很清楚的解释为什么会发生这种情况,而我却不知道。

我有一段在文档中运行的 JavaScript 代码。它创建了一个类似下面这样的对象数组:

var myArray = [{"Id":"guid1","Name":"name1"},{"Id":"guid2","Name":"name2"},...];
如果我在创建数组的地方打印出 JSON.stringify(myArray) 的结果,我会得到我期望的内容。
[{"Id":"guid1","Name":"name1"},{"Id":"guid2","Name":"name2"},...]

但是,如果我试图从一个子文档访问此数组(一个由第一个文档打开的窗口中的文档),那么这个数组就不再是一个数组了。因此,在子文档中执行JSON.stringify(parent.opener.myArray)将导致以下结果:

{"0":{"Id":"guid1","Name":"name1"},"1":{"Id":"guid2","Name":"name2"},...}

但这不是我期望的结果 - 我期望从子文档中获取到与父文档相同的内容。

有人能解释一下为什么会出现这种情况,以及如何修复,使得从子窗口/文档访问该数组仍然是一个数组吗?

PS. 对象并非按上述方式添加到数组中,而是像这样添加的:

function objTemp()
{
    this.Id = '';
    this.Name = '';
};

var myArray = [];

var obj = new ObjTemp();
obj.Id = 'guid1';
obj.Name = 'name1';

myArray[myArray.length] = obj;

如果这有任何影响的话。

非常感谢任何帮助,既可以解决我的问题,也可以更好地理解发生了什么 :)


你在测试哪个浏览器?我记得有一个帖子,IE中发生了完全相同的行为,但在FF中没有。你能提供一个JSFiddle来测试这个问题吗?请参见这里 - neogermi
我无法重现。您在哪个浏览器中测试了?您使用了JSON库吗? - Bergi
我在测试中使用IE,因为这是一个要求。个人而言,我不会选择IE,但这不取决于我。在这个例子中,我只是使用json来字符串化我的警告。对于产生行为的代码,我没有使用任何json库。 - Aidal
如果我在创建数组的文档中执行alert(typeof myArray.sort),我会得到'function'。如果我在子文档中执行alert(typeof parent.opener.myArray.sort),我会得到'object',是的,这是IE浏览器。看起来这正是你链接的帖子所描述的问题。那么该怎么办呢?另一个帖子没有提供答案或解决方案。 - Aidal
嗨,谢谢接受。很高兴知道我的解决方法有用。愉快的编码 :) 顺便为你的问题加1。我也从中学到了东西。 - Elias Van Ootegem
显示剩余2条评论
2个回答

8
最后一行可能是问题所在,你尝试替换myArray[myArray.length] = obj;myArray.push(obj);了吗?可能是因为你显式地创建了一个新索引,将数组转换为了对象...虽然我只是猜测。你能添加子文档使用的检索myArray的代码吗? 编辑 忽略上面的内容,因为它不会有任何区别。尽管如此,我没有想要自夸,但我的想法是正确的。我的想法是,仅使用专有的数组方法,解释器将把它看作是关于myArray类型的线索。问题是:myArray对于父文档来说是一个数组,但由于你正在从一个文档传递数组到另一个文档,这里发生了什么:
数组是一个对象,具有自己的原型和方法。通过将其传递到另一个文档中,你将整个数组对象(值和原型)作为一个对象传递给子文档。在文档之间传递变量时,你实际上是在创建变量的副本(JavaScript 仅在 var 的值被复制的情况下才会复制它的值)。由于数组是一个对象,它的所有属性(和原型方法/属性)都被复制到一个“无名”的Object对象实例中。类似于var copy = new Object(toCopy.constructor(toCopy.valueOf()));正在发生...在父上下文中严格处理数组是最简单的方法,因为解释器知道它是一个数组:
//parent document
function getTheArray(){ return JSON.stringify(myArray);}
//child document:
myArray = JSON.parse(parent.getTheArray());

在这个例子中,变量被转换成字符串形式,但上下文仍然将myArray作为一个真正的JavaScript数组来对待,因此得到的字符串将符合您的预期。将经过JSON编码的字符串从一个文档传递到另一个文档时,它将保持不变,因此JSON.parse()会给您一个精确复制的myArray变量。
请注意,这只是另一个尝试,但我已经更加深入地思考了一下。如果我的理解有误,请指出来,我很乐意学习。如果这被证明是正确的,请告诉我,因为这无疑会在早期或晚期给我带来麻烦。

不,那个语法是创建一个新数组成员的完全有效的方式。虽然 .push() 更具描述性(我更喜欢它),但赋值也可以工作,而且速度更快一些。 - Bergi
我认为数组不是因为我添加项的方式而无效,正如我在帖子中所述,如果我尝试警报数组字面值它看起来是正确的(即它不是一个对象而是一个数组)。如果我从子文档中执行完全相同的操作,则该数组现在是一个对象,而不仅仅是警报var字符串化,但是应该使用数组的代码会抛出错误,因为例如myArray [0] .Id为空(因为它不存在)。 - Aidal
在父页面上将数组转换为字符串,然后在子页面上解析该字符串就可以解决问题了。非常感谢您详尽的回答,我也学到了一些东西 :) - Aidal
在我的情况下,将 .push(x) 替换为 .[i] = x 实际上确实以某种方式保留了 .slice 方法。而使用 stringify 在本地主机上却给我带来了奇怪的“unexpected token A” O_o。 - TrySpace
当将给定的数组分配给另一个窗口中的新对象时,在IE 11中仍存在此问题。 - Jacob Foshee
1
6年后,我仍然偶然发现了这个。在ECMA 6中,还有其他选项吗? - Eagle1

1
请查看本文末尾的链接http://www.karmagination.com/blog/2009/07/29/javascript-kung-fu-object-array-and-literals/,了解此行为和解释的示例。
基本上,这归结于数组是一种本地类型,每个帧都有自己的本地和变量集。
从文章中可以得知:
// in parent window
var a = [];
var b = {};
//inside the iframe
console.log(parent.window.a); // returns array
console.log(parent.window.b); // returns object

alert(parent.window.a instanceof Array); // false
alert(parent.window.b instanceof Object); // false
alert(parent.window.a.constructor === Array); // false
alert(parent.window.b.constructor === Object); // false

您对 JSON.stringify 的调用实际上执行了以下检查(来自 json.js 源代码),似乎没有将其指定为 Array:

        // Is the value an array?
        if (Object.prototype.toString.apply(value) === '[object Array]') {
           //stringify

1
嗯,看起来并没有使用Array.isArray,这会在跨窗口时起作用,而是使用instanceof来引起所描述的行为。 - Bergi
哦,是的,这看起来现在是针对浏览器的,因为json2.js默认只执行浏览器本地可用的stringify版本(在所有现代浏览器中都应该可用)。 - nvuono
我的意思是,尽管你这样说,但代码Object.prototype.toString.apply(value) === '[object Array]'不会失败。 - Bergi

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