为啥这个对象在分配其他东西时不通过引用传递?

Posted

技术标签:

【中文标题】为啥这个对象在分配其他东西时不通过引用传递?【英文标题】:Why isn't this object being passed by reference when assigning something else to it?为什么这个对象在分配其他东西时不通过引用传递? 【发布时间】:2012-03-15 07:55:11 【问题描述】:

我知道在JS中,对象是通过引用传递的,例如:

function test(obj) 
    obj.name = 'new name';


var my_obj =  name: 'foo' ;
test(my_obj);
alert(my_obj.name); // new name

但是为什么下面的不起作用:

function test(obj) 
    obj = ;


var my_obj =  name: 'foo' ;
test(my_obj);
alert(my_obj.name); // foo

我已将对象设置为(空),但它仍然显示foo

谁能解释一下这背后的逻辑?

【问题讨论】:

那是因为对象不是通过引用传递的;) @delnan 但是有。看看currentObject 如何改变$scope.countries -> jsfiddle.net/ay1wpr5L/2 @Imray 这不是通过引用传递的,如下面的多个答案所述。 疯狂的javascript。我喜欢它,同时又讨厌它...... 【参考方案1】:

因为 JavaScript 实际上是通过 pass-by-copy-reference 传递对象。

当您将my_obj 传递给您的test 函数时,会传入对该对象的引用的副本。因此,当您在test 中重新分配对象时,您实际上只是重新分配 copy of 对原始对象的引用;你原来的my_obj 保持不变。

【讨论】:

如果是副本,那么为什么它在我发布的第一个示例中有效?在我的第一个示例中,它也应该保持不变? @Dev555 - 它是对象的引用 的副本 - 我编辑得更清楚了。在您的第一种情况下, obj 是指向您的真实对象的引用的副本。添加属性将正常工作。如果我亲自到场,我可以画一些带箭头的盒子的图片,我认为这会很有帮助:) 那么如何初始化对象呢?最初我有这个instance.initGrid($(instance.mainEntityContainer), instance.mainEntityList); instance.initGrid($(instance.dependantEntityContainer), instance.dependantEntityList);,我不得不将它转换为以下内容:instance.mainEntityList = instance.initGrid($(instance.mainEntityContainer)); instance.dependantEntityList = instance.initGrid($(instance.dependantEntityContainer)); 我仍然想知道如何从以前的初始化中“释放内存”。我是否只需要在某处创建 instance.dependantEntityList = null ?我里面有“= new ...”。【参考方案2】:

如果您熟悉指针,则可以采用这种类比。您实际上是在传递一个指针,因此 obj.someProperty 将取消对该属性的引用并实际覆盖它,而仅覆盖 obj 会杀死指针而不覆盖对象。

【讨论】:

+1 - 做得很好,比我的回答更清晰(假设 OP 知道指针) @AdamRackis 我为你的答案 +1 了,因为它使用了 JS 术语,并让我想起了上面的类比; ) 谢谢这个澄清了:) 为什么我上周搜索这个时没有找到这个答案!?这终于也回答了我的问题,非常感谢! 在这一点上要记住的是,当一个人用一些参数声明一个函数时(比如这里的 obj ), obj 是一种新事物。这就像在那个函数里面说:var obj; // undefined。因此,如果我们正在做一些赋值,我们实际上是在改变函数内部的局部变量,并且我们正在破坏“引用”(指针)。【参考方案3】:

因为您正在覆盖引用,而不是对象。

// Create a new object and assign a reference to it
// to the variable my_obj
var my_obj =  name: 'foo' ;

// Pass the reference to the test function
test(my_obj);

// Assign the reference to a variable called obj
// (since that is the first argument)
function test(obj) 
// Create a new (empty) object and assign a reference to it to obj
// This replaces the existing REFERENCE
    obj = ;

// my_obj still has a reference to the original object, 
// because my_obj wasn't overwritten
alert(my_obj.name); // foo

【讨论】:

您正在覆盖参考的副本,而不是参考。如果您覆盖引用本身,一切都会好起来的。 如果你愿意,你将如何覆盖my_obj @CodyBugstein 将其包裹在另一个对象中。这个问题的另一个答案***.com/a/13452001/841830 显示了这一点,以及其他一些可能的方法。或者,如果你不喜欢这些方法中的任何一种,我认为答案基本上是:你不能覆盖my_obj【参考方案4】:

Javascript 不支持通过引用传递(尽管对象是通过引用传递的,并且只要不被赋值覆盖,引用就会被维护,例如使用 =),但是你可以模仿 C# 的 ref 关键字使用以下技术:

function test(obj) 
  obj.Value = ;
  //obj.Value = name:"changed";


var my_obj =  name: 'foo' ;

(function ()

  my_obj = Value: my_obj;
  var $return = test(my_obj);
  my_obj = my_obj.Value;
  return $return;
).call(this);

alert(my_obj.name); // undefined, as expected
                    // In the question this returns "foo" because
                    // assignment causes dereference

当然,您可以使用全局变量并调用不带参数的函数,在这种情况下不会像这样错过引用:

var obj =  name: 'foo' ;
function test() 
    obj = ;

test();
alert(obj.name); // undefined

如果你的所有代码都关闭了,那么事情就更简单了,比如 globals 不会污染全局命名空间:

(function()
    var obj =  name: 'foo' ;
    function test() 
        obj = ;
    
    test();
    alert(obj.name); // undefined
).call(this);

如果您必须将一些具有 ref 参数的 C# 代码移植到 Javascript,那么上述“闭包内的全局变量”-技术很好。例如。以下 C# 代码:

void MainLoop()

   // ...
   MyStruct pt1 = CreateMyStruct(1);
   MyStruct pt2 = CreateMyStruct(2);
   SwapPoints(ref pt1, ref pt2);
   // ...

void SwapPoints(ref MyStruct pt1, ref MyStruct pt2)

    MyStruct tmp = pt1;
    pt1 = pt2;
    pt2 = tmp;

可以使用以下方式移植到 Javascript:

(function()
    var pt1, pt2;
    function CreateMyStruct(myvar)
    
      return "myvar":myvar  
    
    function MainLoop()
    
       // ...
       pt1 = CreateMyStruct(1);
       pt2 = CreateMyStruct(2);
       console.log("ORIG:",pt1,pt2); 
       SwapPoints(); 
       console.log("SWAPPED:",pt1,pt2);
       // ...
    
    function SwapPoints()
    
        var tmp = pt1;
        pt1 = pt2;
        pt2 = tmp;
    
    MainLoop();

).call(this);

或者如果必须使用局部变量和函数参数,那么解决方案可以基于我回答的第一个示例,如下所示:

(function()
    function CreateMyStruct(myvar)
    
      return "myvar":myvar  
    
    function MainLoop()
    
      // ...
      var pt1 = CreateMyStruct(1);
      var pt2 = CreateMyStruct(2);
      console.log("ORIG:",pt1,pt2); 

      (function ()
      
        pt1 = Value: pt1;
        pt2 = Value: pt2;
        var $return = SwapPoints(pt1, pt2);
        pt1 = pt1.Value;
        pt2 = pt2.Value;
        return $return;
      ).call(this);

      console.log("SWAPPED:",pt1,pt2);
      // ...
    
    function SwapPoints(pt1, pt2)
    
      var tmp = pt1.Value;
      pt1.Value = pt2.Value;
      pt2.Value = tmp;
    
    MainLoop();
).call(this);

真的不得不说Javascript在没有原生ref的时候就差很多了!代码会简单得多。

【讨论】:

以上是关于为啥这个对象在分配其他东西时不通过引用传递?的主要内容,如果未能解决你的问题,请参考以下文章

为啥这个 Ruby 方法通过引用传递它的参数

PHP引用传值

无法通过引用传递typedef结构

为啥这个 ObjC 块在释放时不释放其捕获的引用?包括失败的单元测试

通过引用传递;为啥原始对象没有改变?

在 C# 中通过引用或值传递对象