奇怪的Javascript Array.prototype.constructor.apply行为

3

我在使用JavaScript数组时遇到了奇怪的行为。我的目标是从另一个数组创建一个新数组。我的方法如下:

Array.prototype.constructor.apply(Array, [1,2])

它创建了一个有两个元素[1,2]的新数组,这很好,但当我像这样放置时...

Array.prototype.constructor.apply(Array, [3])

这是创建只有一个元素的数组,例如 [3]... 它会创建:

[undefined,undefined,undefined]

这个代码创建了一个具有3个空元素的数组!你知道为什么吗?我怎样才能用这种方式创建一个新的数组对象呢?

你只是想要复制一个数组吗? - Evan Davis
不,我正在尝试从现有的数组中创建一个新数组... - user1179563
而这与复制不同,因为... - Evan Davis
不太不同:) 我想你是对的 - user1179563
6个回答

9
这是因为使用一个参数nArray构造函数会生成一个由n个元素组成的数组。如果有更多参数,则它将创建一个由这些参数按顺序组成的数组。
实际上,您的代码并未将Array作为构造函数调用:Array.prototype.constructor.apply(Array, [3]) 更像 Array(3)而不是new Array(3)。但是,使用或不使用newArray的行为是相同的。
这所有的问题都引出了一个问题:为什么您要这样做,而不是使用一个数组字面量?如果您想要复制一个数组,使用其slice方法是一个简单的方法:
var a = [1, 2];
var copy = a.slice(0);

抢我的台词了。我也从 Array(3) 调用中得到了 [undefined, undefined, undefined] - Alex W
查看我的答案,Array(3) 会给你一个长度为3的新数组。 - MichaC
a.splice会从a中删除所有条目,这可能不是预期的行为。 - MichaC
@Ela:确实如此。但是,我从未提到过 splice() - Tim Down
@Ela:slice() 不会影响原始数组。 - Tim Down
是的,我的错,我弄混了。 - MichaC

2
Array.prototype.constructor.apply(Array, [1,2])
实际上应该是 Array.apply(null, [1, 2])。当你使用 new Array(1, 2) 时,你并没有将其应用于 Array 构造函数,而是什么都没有应用。因为 Array.prototype.constructor === Array

当我输入 Array.prototype.constructor.apply(Array, [3]) 时,它会创建一个有 3 个空元素的数组!有什么想法吗?

这就是 Array 构造函数 的工作方式。当它被调用时带有单个数字参数时,它将创建具有该 .length 的新空数组(你正在使用 new Array(3))。

我的目标是从另一个数组创建一个新数组。

使用 slice 数组方法 即可实现此功能。
[1, 2].slice();
// or, complicated, but works with array-like objects:
Array.prototype.slice.call([1, 2]);

不是我。实际上,Array.prototype.constructor.apply(Array, [3]) 更像是 Array(3) 而不是 new Array(3),尽管它们的功能是相同的。 - Tim Down
是的,它们做相同的事情,所以我认为添加方便的“new”会强调这一点。 - Bergi

2
Array的第一个构造函数参数是数组的长度,如果传递了该参数,则例如new Array(3)将创建一个具有三个元素的数组,但所有元素都是未定义的。
由于您使用了apply,因此apply的第二个参数是您传递给其应用的方法的参数数组。
这意味着,如果您应用的参数数组仅包含一个元素,则默认构造函数new Array(<numofelemnts>)将被执行。

我认为new也是一个好主意,因为它不会为每个数组复制原型函数。 - Alex W

1
记住,func.apply(obj, [1,2])obj.func(1,2) 是相同的。而 new Array(3) 的结果与你上面给出的一样 - [undefined,undefined,undefined]。这是规范的问题,但它就是这样工作的。你必须传递多于一个参数才能使它们填充数组。

你忘记了thisArgument,它是apply的第一个参数 :-/ - Bergi

0

你只需要在原始数组上使用concat或slice。不必纠结于原型调用,因为那是为创建arguments列表的副本而保留的,以便将其转换为数组:

var new_array_copy = existing_array.slice(0)

我所说的参数功能:

function a_function_with_some_arguments(/* arguments */) {
    // Convert the special arguments list into an array
    var args = Array.prototype.slice.call(arguments, 0);

    // Returns a reference to the new copy of the arguments list.
    return args;
}

0

Mozilla的MDN对于为什么会发生这种情况有很好的描述,但以下是简要说明:

Array对象有两个构造函数。

在您的第一个版本中,您提供了2个参数,Array对象将其理解为“创建具有指定值的数组”。

在您的第二个版本中,您提供了1个参数,Array对象将其理解为“创建一个空数组,并留出空间以容纳参数中指定的数量”。

因此,如果您想要一个包含一个对象的数组,我建议绕过constructor.apply语法,使用更易读的方式(例如var array = [3];)。


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