web-dev-qa-db-fra.com

Pourquoi cet objet n'est-il pas passé par référence lors de l'attribution d'autre chose?

Je sais qu'en JS, les objets sont passés par référence, par exemple:

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

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

Mais pourquoi ne fonctionne pas ci-dessous:

function test(obj) {
    obj = {};
}

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

J'ai défini l'objet sur {} (vide) mais il indique toujours foo.

Quelqu'un peut-il expliquer la logique derrière cela?

53
Dev555

Si vous connaissez les pointeurs, c'est une analogie que vous pouvez faire. Vous passez en fait un pointeur, donc obj.someProperty déréférencerait cette propriété et la remplacerait réellement, alors que simplement remplacer obj tuerait le pointeur et n'écraserait pas l'objet.

66
Alexander Varwijk

Parce que JavaScript passe en fait des objets par passage -copie - référence.

Lorsque vous passez my_obj dans votre fonction test, une copie d'une référence à cet objet est transmise. Par conséquent, lorsque vous réaffectez l'objet dans test, vous ne réattribuez vraiment qu'une copie de une référence à l'objet d'origine; votre original my_obj reste inchangé.

30
Adam Rackis

Parce que vous écrasez la référence, pas l'objet.

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

Javascript ne prend pas en charge le passage par référence (bien que les objets soient passés par référence et que la référence soit conservée tant qu'elle n'est pas remplacée par l'affectation, par exemple en utilisant =), mais vous pouvez imiter le mot clé ref de C # en utilisant la technique suivante:

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

Bien sûr, vous pouvez utiliser des globaux et appeler la fonction sans arguments, auquel cas les références ne sont pas manquées comme ceci:

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

Si vous avez tout votre code en fermeture, alors les choses sont plus simples et surtout comme les globaux ne polluent pas l'espace de noms global:

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

La technique ci-dessus "global dans la fermeture" est sympa si vous devez porter en Javascript du code C # qui a ref arguments. Par exemple. Le code C # suivant:

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;
}

pourrait être porté en Javascript en utilisant quelque chose comme:

(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);

ou s'il est essentiel d'utiliser des variables locales et des arguments de fonction, alors la solution peut être basée sur le premier exemple de ma réponse comme ceci:

(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);

Je dois vraiment dire que Javascript manque beaucoup quand il n'a pas de ref natif! Le code serait beaucoup plus simple.

5
Timo Kähkönen