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

IT技术 javascript pass-by-reference
2021-02-02 15:51:45

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

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

在这一点上要记住的是,当你用一些参数(比如这里的 obj)声明一个函数时, obj 是一种新事物。这就像在说: var obj; // undefined 在那个函数里面。所以如果我们在做一些赋值,我们实际上是在改变函数内部的局部变量,我们正在破坏“引用”(指针)。
2021-03-16 15:51:45
+1 - 做得很好,比我的答案更清楚(假设 OP 知道指针)
2021-03-18 15:51:45
@AdamRackis 我对您的回答进行了 +1,因为它使用了 JS 术语并提到了上面的类比;)
2021-03-21 15:51:45
为什么我上周搜索这个时没有找到这个答案!?这终于也回答了我的问题,非常感谢!
2021-03-24 15:51:45
谢谢这个澄清了:)
2021-04-04 15:51:45

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

当您传入my_obj您的test函数时,会传入对该对象的引用副本。因此,当您在 中重新分配对象时test,实际上只是重新分配了对原始对象的引用副本您的原始文件my_obj保持不变。

如果它是副本,那么它为什么在我发布的第一个示例中起作用?在我的第一个例子中,它也应该保持不变?
2021-03-20 15:51:45
那么如何初始化对象呢?最初我有这个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 ...”里面。
2021-04-09 15:51:45
@ Dev555 - 它是对象引用的副本- 我编辑得更清楚。在您的第一种情况下, obj 是指向您的真实对象的引用的副本。添加属性会正常工作。如果我亲自在场,我可以画一些带箭头的盒子的图片,我认为这会很有帮助:)
2021-04-12 15:51:45

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

// 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
您正在覆盖引用的副本,而不是引用。如果您要覆盖引用本身,一切都会好起来的。
2021-03-22 15:51:45
那么,my_obj如果您愿意,您将如何覆盖
2021-04-03 15:51:45
@CodyBugstein 将它包装在另一个对象中。关于这个问题的另一个答案stackoverflow.com/a/13452001/841830显示了这一点,以及其他一些可能的方法。或者,如果您不喜欢这些方法中的任何一种,我认为答案基本上是:您不能覆盖my_obj.
2021-04-08 15:51:45

Javascript 不支持通过引用传递(尽管对象是通过引用传递的,并且只要没有被赋值覆盖,引用就会被维护,例如 using =),但是您可以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

如果你的所有代码都在闭包中,那么事情就更简单了,就像 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代码会简单得多。