1346 votes

JavaScript est-il un langage pass-by-reference ou pass-by-value ?

Les types primitifs (Number, String, etc.) sont transmis par valeur, mais les Objects sont inconnus, car ils peuvent être à la fois transmis par valeur (dans le cas où l'on considère qu'une variable contenant un objet est en fait une référence à l'objet) et transmis par référence (lorsque l'on considère que la variable de l'objet contient l'objet lui-même).

Bien que cela n'ait pas vraiment d'importance à la fin, je veux savoir quelle est la manière correcte de présenter les arguments en passant par les conventions. Existe-t-il un extrait de la spécification JavaScript qui définit la sémantique à adopter à cet égard ?

1558voto

deworde Points 648

Comme le dit Shog9, c'est intéressant en Javascript.

Prenons cet exemple :

function changeStuff(num, obj1, obj2)
{
    num = num * 10;
    obj1.item = "changed";
    obj2 = {item: "changed"};
}

var num = 10;
var obj1 = {item: "unchanged"};
var obj2 = {item: "unchanged"};
changeStuff(num, obj1, obj2);
console.log(num);
console.log(obj1.item);    
console.log(obj2.item);

Cela produit la sortie :

10
changed
unchanged

S'il s'agissait d'un pur passage par valeur, le changement d'obj1.item n'aurait aucun effet sur l'obj1 en dehors de la fonction. S'il s'agissait d'un pur passage par référence, alors tout aurait changé. num serait 100, et obj2.item indiquerait "changed".

Au lieu de cela, la situation est que l'élément transmis est transmis par valeur. Mais l'élément qui est transmis par valeur est lui-même une référence.

En termes pratiques, cela signifie que si vous modifiez le paramètre lui-même (comme avec num et obj2), cela n'affectera pas l'élément qui a été introduit dans le paramètre. Mais si vous modifiez les INTERNAUX du paramètre, cela se propagera vers le haut (comme avec obj1).

453voto

Tim Goodman Points 7792

C'est toujours un passage par valeur, mais pour les objets, la valeur de la variable est une référence. Pour cette raison, lorsque vous passez un objet et que vous modifiez son membres ces changements persistent en dehors de la fonction. Cela en fait regardez comme le passage par référence. Mais si vous changez réellement la valeur de la variable objet, vous verrez que le changement ne persiste pas, ce qui prouve qu'il s'agit bien d'un passage par valeur.

Exemple :

function changeObject(x) {
  x = {member:"bar"};
  alert("in changeObject: " + x.member);
}

function changeMember(x) {
  x.member = "bar";
  alert("in changeMember: " + x.member);
}

var x = {member:"foo"};

alert("before changeObject: " + x.member);
changeObject(x);
alert("after changeObject: " + x.member); /* change did not persist */

alert("before changeMember: " + x.member);
changeMember(x);
alert("after changeMember: " + x.member); /* change persists */

Sortie :

before changeObject: foo
in changeObject: bar
after changeObject: foo

before changeMember: foo
in changeMember: bar
after changeMember: bar

146voto

Shog9 Points 82052

La variable ne "détient" pas l'objet, elle détient une référence. Vous pouvez assigner cette référence à une autre variable, les deux font alors référence au même objet. Il s'agit toujours d'un passage par valeur (même lorsque cette valeur est une référence...).

Il n'y a aucun moyen de modifier la valeur d'une variable passée en paramètre, ce qui serait possible si JS supportait le passage par référence.

102voto

Ray Perea Points 525

Mes 2 Cents.... C'est ainsi que je le comprends. (N'hésitez pas à me corriger si je me trompe)

Il est temps de jeter tout ce que vous savez sur le passage par valeur / référence. Parce qu'en Javascript, cela n'a pas d'importance si c'est passé par valeur ou par référence ou autre.

Ce qui compte, c'est la mutation par rapport à l'affectation des paramètres passés dans une fonction.

OK, laissez-moi faire de mon mieux pour expliquer ce que je veux dire. Disons que vous avez quelques objets.

var object1 = {};
var object2 = {};

Ce que nous avons fait est une "affectation"... Nous avons assigné 2 objets vides distincts aux variables "object1" et "object2".

Maintenant, disons que nous préférons l'objet 1... Donc, nous "assignons" une nouvelle variable.

var favoriteObject = object1;

Ensuite, pour une raison quelconque, nous décidons que nous préférons l'objet 2. Donc, nous faisons simplement une petite réaffectation.

favoriteObject = object2;

Rien n'est arrivé à l'objet1 ou à l'objet2. Nous n'avons changé aucune donnée du tout. Tout ce que nous avons fait, c'est réassigner ce qu'est notre objet préféré. Il est important de savoir que object2 et favoriteObject sont tous deux affectés au même objet. Nous pouvons modifier cet objet via l'une ou l'autre de ces variables.

object2.name = 'Fred';
console.log(favoriteObject.name) // logs Fred
favoriteObject.name = 'Joe';
console.log(object2.name); // logs Joe 

OK, maintenant regardons les primitives comme les chaînes de caractères par exemple.

var string1 = 'Hello world';
var string2 = 'Goodbye world';

Encore une fois, nous choisissons un favori.

var favoriteString = string1;

Nos deux variables favoriteString et string1 sont affectées à 'Hello world'. Maintenant, que se passe-t-il si nous voulons changer notre favoriteString ? ?? Que se passera-t-il ?

favoriteString = 'Hello everyone';
console.log(favoriteString); // Logs 'Hello everyone'
console.log(string1); // Logs 'Hello world'

Uh oh.... Ce qui s'est passé. Nous n'avons pas pu changer la chaîne 1 en changeant la chaîne favorite... Pourquoi ? parce que les chaînes de caractères sont immuables et que nous ne l'avons pas mutée. Tout ce que nous avons fait est de "RE ASSIGNER" favoriteString à une nouvelle chaîne. Cela l'a essentiellement déconnectée de string1. Dans l'exemple précédent, lorsque nous avons renommé notre objet, nous n'avons rien assigné. Au lieu de cela, nous avons simplement muté l'objet, ce qui maintient les connexions entre les deux variables et les objets sous-jacents.

Maintenant, passons aux fonctions et au passage des paramètres.... Lorsque vous appelez une fonction et que vous lui passez un paramètre, vous effectuez essentiellement une "affectation" à une nouvelle variable, et cela fonctionne exactement de la même manière que si vous aviez simplement affecté en utilisant le signe égal (=).

Prenez ces exemples.

var myString = 'hello';

// Assign to a new variable (just like when you pass to a function)
var param1 = myString; 
param1 = 'world'; // Re assignment

console.log(myString); // logs 'hello'
console.log(param1);   // logs 'world'

Maintenant, la même chose, mais avec une fonction

function myFunc(param1) {
    param1 = 'world';
}

var myString = 'hello';
// Calls myFunc and assigns param1 to myString just like param1 = myString
myFunc(myString); 

console.log(myString); // logs 'hello'
console.log(param1);   // logs 'world'

OK, maintenant donnons quelques exemples en utilisant des objets à la place... d'abord, sans la fonction.

var myObject = {
    firstName: 'Joe',
    lastName: 'Smith'
};

// Assign to a new variable (just like when you pass to a function)
var otherObj = myObject;

// Let's mutate our object
otherObj.firstName = 'Sue'; // I guess Joe decided to be a girl

console.log(myObject.firstName); // Logs 'Sue'
console.log(otherObj.firstName); // Logs 'Sue'

// Now, let's reassign
otherObj = {
    firstName: 'Jack',
    lastName: 'Frost'
};

// Now, otherObj and myObject are assigned to 2 very different objects
// And mutating one object no longer mutates the other
console.log(myObject.firstName); // Logs 'Sue'
console.log(otherObj.firstName); // Logs 'Jack';

Maintenant, la même chose, mais avec un appel de fonction

function myFunc(otherObj) {

    // Let's mutate our object
    otherObj.firstName = 'Sue';
    console.log(otherObj.firstName); // Logs 'Sue'

    // Now let's re-assign
    otherObj = {
        firstName: 'Jack',
        lastName: 'Frost'
    };
    console.log(otherObj.firstName); // Logs 'Jack'

    // Again, otherObj and myObject are assigned to 2 very different objects
    // And mutating one object no longer mutates the other
}

var myObject = {
    firstName: 'Joe',
    lastName: 'Smith'
};

// Calls myFunc and assigns otherObj to myObject just like otherObj = myObject
myFunc(myObject);

console.log(myObject.firstName); // Logs 'Sue', just like before

OK, si vous avez lu l'intégralité de cet article, vous avez peut-être maintenant une meilleure compréhension du fonctionnement des appels de fonction en javascript. Peu importe si quelque chose est passé par référence ou par valeur... Ce qui compte c'est l'assignation vs la mutation.

Chaque fois que vous passez une variable à une fonction, vous "affectez" le nom de la variable du paramètre, comme si vous utilisiez le signe égal (=).

N'oubliez jamais que le signe égal (=) signifie une affectation. N'oubliez jamais que le fait de passer un paramètre à une fonction signifie également une affectation. C'est la même chose et les 2 variables sont reliées exactement de la même manière.

La seule fois où la modification d'une variable affecte une autre variable est lorsque l'objet sous-jacent est muté.

Il est inutile de faire une distinction entre les objets et les primitives, car cela fonctionne exactement de la même manière que si vous n'aviez pas de fonction et que vous utilisiez simplement le signe égal pour assigner à une nouvelle variable.

Le seul problème est que le nom de la variable que vous passez dans la fonction est le même que le nom du paramètre de la fonction. Dans ce cas, vous devez traiter le paramètre dans la fonction comme s'il s'agissait d'une toute nouvelle variable privée à la fonction (car c'est le cas).

function myFunc(myString) {
    // myString is private and does not affect the outer variable
    myString = 'hello';
}

var myString = 'test';
myString = myString; // Does nothing, myString is still 'test';

myFunc(myString);
console.log(myString); // logs 'test'

23voto

user779764 Points 106

Un objet extérieur à une fonction est passé dans une fonction en donnant une référence à l'obejct extérieur. Lorsque l'on utilise cette référence pour manipuler son objet, l'objet extérieur est donc affecté. Cependant, si à l'intérieur de la fonction vous décidez de diriger la référence vers quelque chose d'autre, vous n'avez pas du tout affecté l'objet extérieur, car tout ce que vous avez fait est de rediriger la référence vers quelque chose d'autre.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X