JavaScript中如何将对象作为引用传递

58

我有一个对象,它在函数内部传递到许多不同的函数中。这些函数可能会改变对象的值,但如果它们确实改变了它,那么我希望获取对象最新的更改。

以下是我想要做的:

var ob = {text: 'this is me', name: 'john'}

function (object) {

     changeObject(object);
     customObjectChanger(object);
     callback = function (object) {
          object.text = 'new text';
     }

     callback(object);

     // object value here should be object{text: 'new text', name: 'john'};    
}

2
Javascript在任何时候都会自动按引用传递对象。你尝试过你上面的代码,看它是否已经做到了你想要的吗? - jcsanyi
没有,我已经阅读了它。只是想在这里确认一下。 - Basit
3
JavaScript不是按引用传递(Pass By Reference)的。然而,当对象被传递或赋值时,JavaScript不会复制对象。因此,它是一个具有不同名称的相同对象——对该对象所做的更改(来自任何名称)会影响该对象本身。 - user2246674
3
这取决于你所说的“按引用传递”的意思。在我看来,“用不同的名称指代同一个对象”正是引用的定义。 - jcsanyi
@jcsanyi这就是为什么定义术语和避免歧义非常重要的原因。(维基百科文章确实承认该术语存在歧义,这就是为什么我尽量避免使用“按引用传递”这个术语,除非在那些无疑地意味着改变本地名称绑定会影响调用者的情况下。) - user2246674
5个回答

174

在JavaScript中,对象始终通过复制-引用传递。我不确定这是否是完全正确的术语,但将对象引用的副本传递。

这意味着在函数执行完成后,您将能够看到对对象所做的任何更改。

代码:

var obj = {
  a: "hello"
};

function modify(o) {
  o.a += " world";
}

modify(obj);
console.log(obj.a); //prints "hello world"

话虽如此,由于传递的只是参考副本,如果在函数内重新分配对象,则外部不会看到此更改,因为您更改的是参考的副本。

代码:


var obj = {
  a: "hello"
};

function modify(o) {
  o = {
    a: "hello world"
  };
}

modify(obj);
console.log(obj.a); //prints just "hello"


2
我更喜欢“共享对象调用”这种叫法,因为它直接明了,将实现关注点与其他调用约定分离,并且减少了歧义。在ECMAScript规范的任何部分中都没有使用“引用”来表示这个目的。(引用规范类型不适用于函数调用。) - user2246674
4
对于传统的按引用传递,与之不同的清晰描述值得点赞。 - jcsanyi
2
我认为我已经清楚地表明了传递的是对象的引用副本,而不是对象本身。 - Adam Rackis
6
我可以处理这个问题吗?如果我想改变对象本身而不是其副本引用怎么办? - abhinav1602
1
要更新对象本身,您可以使用 Object.assign(o, newObject); - 8ctopus
显示剩余8条评论

7

这是有关JavaScript中的按值传递和按引用传递的更多解释。

在这个概念中,他们讨论了通过引用传递变量和通过值传递变量。

按值传递(原始类型)

var a = 3;
var b = a;

console.log(a); // a = 3
console.log(b); // b = 3

a=4;
console.log(a); // a = 4
console.log(b); // b = 3
  • 适用于JS中的所有原始类型(string、number、boolean、undefined、null)。
  • a被分配了一个内存(比如0x001),而b创建了一个值在内存中的副本(比如0x002)。
  • 因此,改变一个变量的值不会影响另一个变量,因为它们都存在于两个不同的位置。

按引用传递(对象)

var c = { "name" : "john" };    
var d = c;

console.log(c); // { "name" : "john" }
console.log(d); // { "name" : "john" }

c.name = "doe"; 

console.log(c); // { "name" : "doe" }    
console.log(d); // { "name" : "doe" }
  • JS引擎将对象分配给变量c,它指向某些内存,比如(0x012)
  • d=c时,在此步骤中,d指向相同的位置(0x012)。
  • 更改任何一个值都会更改两个变量的值。
  • 函数是对象。

特殊情况,按引用传递(对象)

c = {"name" : "jane"}; 
console.log(c); // { "name" : "jane" }    
console.log(d); // { "name" : "doe" }
  • 等于(=)操作符会设置新的内存空间或地址。

7

JavaScript中的“对象”不是值,也不能被“传递”。

你要处理的所有值都是引用(指向对象的指针)。

传递或分配引用会产生另一个引用,指向同一对象。当然,你可以通过那个其他引用修改相同的对象。


3
确实 - 因此对象的引用副本会被“传递” -_- - Adam Rackis
实际上,对象本身不会被传递,但是引用会被传递,如果你在接收函数内部修改了它的属性,那么你可以修改它们。 - Ignacio
简洁明了的回答。当对象被赋值给一个变量时,所赋的值是该对象的引用。因此,所有变量都保存了一个值,但有些值是对象(例如对象、函数、数组)的引用,而有些则是字面值(例如数字、字符串、布尔值和其他原语)。 - RobG

0
如何处理这个问题?如果我想改变对象本身而不是它的副本引用怎么办?
我想在函数内重新分配一个对象,并且它的新值在调用函数中可见。
除了接受的答案之外,为了回答评论中未解决的问题,这是我对通过引用传递的看法,同时关注实践中的作用域/闭包

const obj = {
  count: 0,
}

function incrementReference(_obj) {
  obj.count++
}

function createIncrementByCopy(_obj) {
  let inner = {}
  Object.assign(inner, _obj)

  return function increment() {
    inner.count++
  }
}

function createScopedIncrement(_obj) {
  return function increment() {
    _obj.count++
  }
}
function createExtend(_obj) {
  return function increment(ext) {
    Object.assign(_obj, ext)
  }
}

console.log('obj', obj) // { count: 0 }

incrementReference(obj)
console.log('incrementReference ✅', obj.count) // 1

const incrementCopy = createIncrementByCopy(obj)
incrementCopy()
console.log('incrementCopy ❌', obj.count) // 1

const incrementA = createScopedIncrement(obj)
incrementA()
console.log('incrementA ✅', obj.count) // 2

const extendObj = createExtend(obj)
extendObj({ text: 'New Property!' })
console.log('extendObj ✅', obj) // { count: 2, text: 'New Property!' } 

以下文章讨论了一个常见的术语混淆,我实在不太清楚https://dev.to/bytebodger/a-gotcha-of-javascript-s-pass-by-reference-1afm
相关(良好的上下文,更复杂,不同的用例):https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign#copying_accessors

0
基本上,在JavaScript中,不存在按引用传递的概念,因为按值传递的目的已经足够使JS变得如此特殊。当我们在JavaScript中传递一个对象时: 1)函数需要使用该值,该值是引用副本 2)这个值指向原始对象的地址。

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