在Javascript中交换整个数组

3
当我试图编写一个交换两个数组的函数时,原始数组并没有被改变。
function swap(x, y) {
    var temp = x; x = y; y = temp;
}

u=[1, 0];
v=[0, 1];
swap(u, v);
console.log(u);
console.log(v);

这将导致u为[1, 0],v为[0, 1]。在调用swap函数后,这些值没有改变。
另一方面,如果我不使用函数调用:
u=[1, 0];
v=[0, 1];
var temp = u;
u = v;
v = temp;
console.log(u);
console.log(v);

然后它们将被正确地交换,u变成 [0, 1],v变成 [1, 0]。

我以为Javascript数组是按引用传递的,而不是按值传递的。这里我是否有什么误解?


预期输出是什么? - thefourtheye
u和v应该交换。因此,u应该是[0, 1],v应该是[1, 0]。我也会在问题中更清楚地表述。 - user3448821
数组不会被复制,这并不意味着它们是按“引用传递”的方式传递。这更类似于按指针传递。 - The Paramagnetic Croissant
“我以为Javascript数组是按引用传递的,而不是按值传递的。我有什么误解吗?” 是的,JavaScript只有按值传递。值的类型是无关紧要的。 - newacct
6个回答

8
JavaScript无法传递对“u”和“v”变量本身的引用。因此,在您的“swap()”函数中,对“x”或“y”的任何赋值都不会改变分配给“u”或“v”的内容。JavaScript传递到“u”和“v”保存的对象的引用。因此,您无法从“swap()”内更改“u”和“v”变量。您可以更改它们指向的对象的内容,因此可以修改“u”和“v”指向的对象的属性。

由于我具有C/C++背景,因此我认为在将对象传递为参数时,JavaScript所做的类似于“按指针传递”。当您调用“swap(u,v)”时,传递给“swap()”函数的是指向“u”也指向的数组的指针。因此,现在您有两个变量“u”和“x”,都“指向”同一数组。因此,如果您修改实际数组,则由于“u”指向该相同数组,两者都将看到修改。但是,“swap()”函数内部无论做什么都不能改变对象“u”或“v”实际指向的内容。


在JavaScript中,更改原始变量指向的对象的唯一方法是将它们作为对象的属性,并像这样传递对象:

function swap(obj, x, y) {
    var temp = obj[x]; obj[x] = obj[y]; obj[y] = temp;
}

var container = {};
container.u = [1, 0];
container.v = [0, 1];
swap(container, "u", "v");
console.log(container.u);
console.log(container.v);

如果您不介意从头重新编写两个数组,您可以将一个数组的所有内容复制到一个临时数组中,然后将一个数组覆盖到另一个数组上,最后将临时数组的内容复制回第一个原始数组。这种方法并不是很高效,而且可能有更好的方法来解决您的原始问题,但是它是可行的。

 function swap(x, y) {
      // remove all elements from x into a temporary array
      var temp = x.splice(0, x.length);
      // then copy y into x
      x.push.apply(x, y);
      // clear y, then copy temp into it
      y.length = 0;
      y.push.apply(y, temp);
 }

我已经在C++中使用了std::swap(),在Javascript中有没有一种方法可以实现相同的功能呢? - user3448821
这是真正的按引用传递。在像C这样的语言中,你只能按值传递,但你可以通过值传递引用,因此你也可以通过值传递引用到引用,这允许OP期望的行为。 - Paul
1
@user3448821 - 在Javascript中,模拟真正的引用并允许您更改原始变量包含内容的唯一方法是通过将uv作为对象的属性并传递包含对象来实现。然后,您可以交换这两个属性的内容。 - jfriend00
@user3448821 - 我在我的回答中添加了一个使用对象进行交换的示例。 - jfriend00
@Paulpro - 我的错误。我会纠正的。JavaScript让我感到很烦恼,因为它并不清楚哪些数组方法会修改数组,哪些不会,并且名称往往会误导你。这不是我第一次搞砸了。 - jfriend00
显示剩余4条评论

1

在“引用/值”问题上,术语很棘手,但我会尽力解释。

对于对象/数组变量,您拥有的实际上只是引用。与C++不同,“x=y”并不会将对象的变量复制到新的内存位置。在内部,它只是复制指针位置。语言没有构造函数来“自动重新创建”像对象或数组这样的东西;如果出于某种原因,您想要维护两个副本的数组,则需要显式创建它,然后复制值(即= [];= new Array(); 或类似= oldArray.map(...)的复制函数)

以下是一个小的代码示例,可能有助于理解概念。这些规则也适用于对象和数组之间。

a = {}; // In case you haven't seen it, this is like shorthand of "new Object();"
b = {};
c = b;
console.log(a === b); // false
console.log(b === c); // true
b.property = "hello";
console.log(c.property) // hello

与C++不同,说“x = y”实际上并没有将对象的变量复制到一个新的内存位置中。它与C++类似--在C++中,它将是Foo *y = ...; Foo *x = y;结构相同。 - newacct
@newacct 我认为我们用不同的词汇表达了相同的意思,但我不确定我同意相似之处。在你的两行代码示例中,你声明了一个包含指针的变量。然后,你创建了另一个变量,并且从我的角度来看,将该指针复制到一个新的内存位置。编译器确实必须为这些新的32/64位分配空间。因此,如果你总是只使用指针来处理每个对象(我知道这是一种危险的做法,不应该随意应用),那么它就像C++。 - Katana314
但这正是JavaScript代码所说的。在JavaScript中,类型只是没有明确写出来。 - newacct

1
与Java一样,JavaScript仅支持值传递。在函数中将值分配给本地变量不会对函数外的任何内容产生影响。

0

它们是通过引用传递的,但也是通过引用分配的。当你写 x = y 时,你并没有修改任何一个数组,你只是让你的本地变量 x 引用与 y 相同的数组。

如果你想交换数组内容,你必须修改数组本身:

function swap(x,y) {
    var temp = x.slice(0);
    x.length = 0;
    [].push.apply( x, y );
    y.length = 0;
    [].push.apply( y, temp ); 
}

只是为了明确,这并不是真正的交换。这是从头开始重写两个数组,效率并不特别高。也许这就是OP想要的,但我肯定不会把这称为通常意义上的交换。 - jfriend00
@jfriend00 同意。这个答案的时间复杂度是 Θ(n),而你使用容器对象的解决方案则是 Θ(1)。它还会使用额外的内存,因为它会复制 x。对于小数组来说,它们的速度大致相同,但对于大数组来说,你的解决方案肯定会更快。我认为这个版本更易于阅读和使用。 - Paul
它们不是通过引用传递的。语义与Java完全相同。而且在Stack Overflow上普遍描述Java没有按引用传递。 - newacct

0

2022年2月交换两个完整数组内容的解决方案。

您可以使用解构来交换这两个数组:

let a = [  1,  2,  3, 4 ];
let b = ['a','b','c','d'];

[a,b] = [b,a];   // swap

console.log(a);  // ["a", "b", "c", "d"]
console.log(b);  // [1, 2, 3, 4, 5]

    let a = [  1,  2,  3, 4 ];
    let b = ['a','b','c','d'];
    
    [a,b] = [b,a];   // swap
    
    console.log(a);  // ["a", "b", "c", "d"]
    console.log(b);  // [1, 2, 3, 4, 5]


0

要交换两个数组,您可以使用以下代码:

版本1

let arrA = [1,2,3,4];
let arrB = ['Eve', 'Bar', 'Foo'];

let tempArr = [arrA, arrB];  // constructing new array
arrA = tempArr [1];
arrB = tempArr [0];

版本 1(简写)

let arrA = [1,2,3,4];
let arrB = ['Eve', 'Bar', 'Foo'];
    
// RHS : construction a new array 
// LHS : destruction of array
[arrB, arrA ] = [arrA, arrB];  

版本2(展开运算符)

let arrA = [1,2,3,4];
let arrB = ['Eve', 'Bar', 'Foo'];

let arrC = [...arrB]
arrB = [...arrA]
arrA = [...arrC]

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