【问题标题】:Swapping objects by reference?通过引用交换对象?
【发布时间】:2016-08-25 07:27:20
【问题描述】:

我希望使用 swap 函数交换两个对象,并仅引用它们。

一个典型的用例是这样的:

function swap(a, b) {
    ...
}

var a = [ { id: 1 }, { id: 2 } ]
swap(a[0], a[0])
console.log(a) // [ { id: 2 }, { id: 1 } ]

var one = { id: 1 }
var two = { id: 2 }
swap(one, two)
console.log(one) // Should be { id: 2 }
console.log(two) // Should be { id: 1 }

(添加了更多用例。swap 不应依赖于任何数组功能。)

这可能吗?我知道 javascript 的功能是复制引用source。所以本质上,swap(a, b)ab 只是a[0]a[1] 的副本,所以如果不实际将a 的字段复制到b 的字段,反之亦然,这似乎不切实际。


编辑:@timolawl 指出了 ES6 的解构,但它似乎不适用于这个用例:

let a = [{ id: 1 }, { id: 2 }]

let one = a[0]
let two = a[1]

console.log(a[0])
console.log(a[1])

[one, two] = [two, one]

console.log(a[0])
console.log(a[1])

jsfiddle

【问题讨论】:

  • 可能想要编辑最后一部分以说明这不是代码本身的错,而是因为解析器必须猜测 ASI 的位置。第一个 console.log(a[1]) 之后的分号解决了这个问题,因为解析器错误地将代码假定为 console.log(a[1])[one...

标签: javascript


【解决方案1】:

使用 ES6,你可以使用destructuring:

一般情况:

[a, b] = [b, a];

例子:

let one = { id: 1 };
let two = { id: 2 };

document.body.insertAdjacentHTML('beforeend', '<pre>' + JSON.stringify(one, 0, 4) + '</pre>');
document.body.insertAdjacentHTML('beforeend', '<pre>' + JSON.stringify(two, 0, 4) + '</pre>');

[one, two] = [two, one];

document.body.insertAdjacentHTML('beforeend', '<pre>' + JSON.stringify(one, 0, 4) + '</pre>');
document.body.insertAdjacentHTML('beforeend', '<pre>' + JSON.stringify(two, 0, 4) + '</pre>');

【讨论】:

  • 抱歉,不能使用数组功能。我没有正确定义用例的错。我在原始问题中进行了编辑,以显示它应该能够处理哪些其他用例。很高兴学习一个新的 ES6 函数:)
  • @KeithYong 它不必与数组一起使用。如上所示,您可以仅将其与两个普通变量一起使用。这只是语法! :)
  • @timolawl 这种行为是否始终一致?例如,当第一次阅读时,我认为它可以实现为one = two 然后two = one,结果都将成为原始two
  • @qtuan 是的,交换行为是一致的。这是来自 MDN 关于在解构中交换变量的文章:“没有解构赋值,交换两个值需要一个临时变量(或 XOR 交换)”
  • @timolawl 您的回答对我来说是完美的,但是在交换存储在数组中的对象时遇到了问题。我正在尝试调试它,但你也可以调查一下吗? jsfiddle.net/terda12/w03oyuyg
【解决方案2】:

通过深拷贝交换

没有简单的方法。 Object.assign 可以提供帮助,但链下的任何属性都只是浅拷贝。还有一个笨拙的 JSON 副本。 var b = JSON.parse(JSON.stringify(a)); 但这不处理函数并忽略所有未分配的属性,并且不能处理任何循环数据(这都是通用的)。您可以迭代属性和原型链并使用 Object.hasOwnProperty,但对于副本应该有多深、如何锻炼继承等等,您仍然有未知数

通常,副本(或交换)需要了解数据的含义和上下文。有些人可能会认为这是一个不利于 Javascript 的短暂失败,但随后的通用交换或复制功能打破了函数式编程、数据封装等的许多规则,所有这些规则背后都有很好的理由。

这就是为什么没有提供方便的深拷贝的原因。

【讨论】:

  • 请不要对对象进行字符串化+解析来复制它。那是非常低效的
  • @Downgoat LOL 别担心,我什至不能让自己返回数组或对象字面量,必须始终预先分配返回对象并在函数参数中保留 GC 的位置和别挡我的路。当你看到什么被归类为好的 javascript 时,它只是值得畏缩。 Stringify + parse 在数组 concat 的映射和大多数人所做的所有其他低效的疯狂行为中非常适合。
【解决方案3】:

可以重构这个函数来完成你想要的——提供数组和两个索引并基于这些进行交换。例如:

function swap(array, a, b) {
    var temp = array[a]; // store a temporary copy of the variable

    // perform the swap
    array[a] = array[b];
    array[b] = temp;
}

请注意,临时副本是必需的,因为设置array[a] = array[b] 时会覆盖数据 - 因此您必须事先存储array[a] 的值。

【讨论】:

  • 这对我来说不是一个选择,因为我将在复杂的状态树上为 React 应用程序交换。我正在尝试交换状态树中的两个对象,无论它们位于何处。
  • @KeithYong 这不太可能,因为 JavaScript 没有指针/引用的概念。
猜你喜欢
  • 2019-12-10
  • 1970-01-01
  • 2017-09-11
  • 2012-08-23
  • 1970-01-01
  • 1970-01-01
  • 2011-07-06
  • 2012-07-07
相关资源
最近更新 更多