为对象分配其他内容时,为什么不通过引用传递此对象?

时间:2012-02-24 21:02:00

标签: javascript pass-by-reference

我知道在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

任何人都可以解释这背后的逻辑吗?

4 个答案:

答案 0 :(得分:66)

如果您熟悉指针,那么您可以采用类比。你实际上是在传递一个指针,所以obj.someProperty将取消引用该属性并实际覆盖它,而只是覆盖obj会终止指针而不会覆盖该对象。

答案 1 :(得分:30)

因为JavaScript实际上是通过传递复制 - 参考传递对象。

当您将my_obj传递到test函数时,会传入对该对象的引用的副本。因此,当您重新分配对象时在test中,您实际上只是重新分配副本对原始对象的引用;您的原始my_obj仍未更改。

答案 2 :(得分:27)

因为你要覆盖引用而不是对象。

// 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

答案 3 :(得分:5)

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

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

如果你的所有代码都在闭包中,那么事情就更简单了,就像全局变量没有污染全局命名空间一样:

(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的情况下缺少多少!代码会简单得多。