web-dev-qa-db-fra.com

Est-ce que Javascript passe par référence?

Javascript passe-t-il par références ou par valeurs? Voici un exemple tiré de Javascript: The Good Parts . Je suis très confus à propos du paramètre my pour la fonction rectangle. Il s'agit en fait de undefined et redéfini à l'intérieur de la fonction. Il n'y a pas de référence originale. Si je le supprime du paramètre function, la fonction inside area ne pourra pas y accéder. 

Est-ce une fermeture? Mais aucune fonction n'est retournée.

var shape = function (config) {
    var that = {};
    that.name = config.name || "";
    that.area = function () {
        return 0;
    };
    return that;
};
var rectangle = function (config, my) {
    my = my || {};
    my.l = config.length || 1;
    my.w = config.width || 1;
    var that = shape(config);
    that.area = function () {
        return my.l * my.w;
    };
    return that;
};
myShape = shape({
    name: "Unhnown"
});
myRec = rectangle({
    name: "Rectangle",
    length: 4,
    width: 6
});
console.log(myShape.name + " area is " + myShape.area() + " " + myRec.name + " area is " + myRec.area());
307
J Any

Les primitives sont passées par valeur, les objets sont passés par "copie d'une référence".

Plus précisément, lorsque vous transmettez un objet (ou un tableau), vous transmettez (de manière invisible) une référence à cet objet et il est possible de modifier le contenus de cet objet, mais si vous essayez de remplacer la référence, cela ne le affecte la copie de la référence détenue par l'appelant - c'est-à-dire que la référence elle-même est passée par valeur:

function replace(ref) {
    ref = {};           // this code does _not_ affect the object passed
}

function update(ref) {
    ref.key = 'newvalue';  // this code _does_ affect the _contents_ of the object
}

var a = { key: 'value' };
replace(a);  // a still has its original value - it's unmodfied
update(a);   // the _contents_ of 'a' are changed
565
Alnitak

Pensez-y comme ceci:

Chaque fois que vous créez un objet dans ECMAscript, cet objet est formé dans un emplacement mystique ECMAscript où aucun homme ne pourra jamais accéder. Tout ce que vous récupérez est une référence à cet objet dans cet endroit mystique.

var obj = { };

Même obj est seulement une référence à l'objet (qui se trouve dans cet endroit merveilleux et spécial) et par conséquent, vous ne pouvez que faire passer cette reference around. En effet, tout morceau de code qui accède àobjmodifiera leobjetqui est très très loin.

53
jAndy

My 2 Cents .... Peu importe que Javascript transmette les paramètres par référence ou par valeur. Ce qui compte vraiment, c’est la cession vs la mutation.

J'ai écrit une explication plus longue et plus détaillée ici ( JavaScript est-il un langage passe-à-référence ou passe-à-valeur? )

Lorsque vous transmettez quelque chose (que ce soit un objet ou une primitive), tout le javascript consiste à assigner une nouvelle variable dans la fonction ... comme si vous utilisiez le signe égal (=)

Le comportement de ce paramètre dans la fonction est exactement le même que si vous affectiez une nouvelle variable à l'aide du signe égal. Prenez ces exemples simples.

var myString = 'Test string 1';

// Assignment - A link to the same place as myString
var sameString = myString;

// If I change sameString, it will not modify myString, 
// it just re-assigns it to a whole new string
sameString = 'New string';

console.log(myString); // logs 'Test string 1';
console.log(sameString); // logs 'New string';

Si je transmettais myString en tant que paramètre à une fonction, il se comporterait comme si je l'assignais simplement à une nouvelle variable. Maintenant, faisons la même chose, mais avec une fonction au lieu d'une simple affectation

function myFunc(sameString) {

    // Re assignment.. again, it will not modify myString
    sameString = 'New string';
}

var myString = 'Test string 1';

// This behaves the same as if we said sameString = myString
myFunc(myString);

console.log(myString); // Again, logs 'Test string 1';

La seule raison pour laquelle vous pouvez modifier des objets lorsque vous les transmettez à une fonction est que vous ne réaffectez pas ... Au lieu de cela, les objets peuvent être modifiés ou mutés ... Encore une fois, cela fonctionne de la même manière.

var myObject = { name: 'Joe'; }

// Assignment - We simply link to the same object
var sameObject = myObject;

// This time, we can mutate it. So a change to myObject affects sameObject and visa versa
myObject.name = 'Jack';
console.log(sameObject.name); // Logs 'Jack'

sameObject.name = 'Jill';
console.log(myObject.name); // Logs 'Jill'

// If we re-assign it, the link is lost
sameObject = { name: 'Howard' };
console.log(myObject.name); // Logs 'Jill'

Si je transmettais myObject en tant que paramètre à une fonction, il se comporterait comme si je l'assignais simplement à une nouvelle variable. Encore une fois, la même chose avec exactement le même comportement mais avec une fonction.

function myFunc(sameObject) {

    // We mutate the object, so the myObject gets the change too... just like before.
    sameObject.name = 'Jill';

    // But, if we re-assign it, the link is lost
    sameObject = { name: 'Howard' };
}

var myObject = { name: 'Joe'; }

// This behaves the same as if we said sameObject = myObject;
myFunc(myObject);
console.log(myObject.name); // Logs 'Jill'

Chaque fois que vous transmettez une variable à une fonction, vous affectez quel que soit le nom du paramètre, comme si vous utilisiez le signe égal (=).

Rappelez-vous toujours que le signe égal (=) signifie affectation . Et transmettre un paramètre à une fonction signifie également affectation. Ils sont identiques et les 2 variables sont connectées exactement de la même manière.

La seule fois où la modification d'une variable affecte une variable différente 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 utilisiez simplement le signe égal pour assigner une nouvelle variable.

36
Ray Perea

Comme avec C, au final, tout est passé par valeur. Contrairement à C, vous ne pouvez pas réellement sauvegarder et transmettre l’emplacement d’une variable, car elle ne contient pas uniquement des pointeurs.

Et ses références concernent tous des objets, pas des variables. Il existe plusieurs façons d'obtenir le même résultat, mais elles doivent être effectuées à la main, et non pas simplement en ajoutant un mot clé sur le site de l'appel ou de la déclaration.

14
jmoreno

Les arguments de fonction sont passés soit par valeur, soit par partage, mais jamais JAMAIS par référence en Javascript!

Call-by-Value

Les types primitifs sont passés par valeur:

var num = 123, str = "foo";

function f(num, str) {
  num += 1;
  str += "bar";
  console.log("inside of f:", num, str);
}

f(num, str);
console.log("outside of f:", num, str);

Les réassignations à l'intérieur d'une étendue de fonction ne sont pas visibles dans l'étendue environnante.

Ceci s'applique également à Strings, qui sont un type de données composite et pourtant immuable:

var str = "foo";

function f(str) {
  str[0] = "b"; // doesn't work, because strings are immutable
  console.log("inside of f:", str);
}

f(str);
console.log("outside of f:", str);

Appel par partage

Les objets, c'est-à-dire que tous les types qui ne sont pas des primitives sont passés par partage. Une variable qui contient une référence à un objet ne contient en réalité qu'une copie de cette référence. Si Javascript poursuivait une stratégie d'évaluation d'appel par référence, la variable contiendrait la référence d'origine. C'est la différence cruciale entre le partage et le renvoi.

Quelles sont les conséquences pratiques de cette distinction?

var o = {x: "foo"}, p = {y: 123};

function f(o, p) {
  o.x = "bar"; // mutation
  p = {x: 456}; // reassignment
  console.log("o inside of f:", o);
  console.log("p inside of f:", p);
}

f(o, p);

console.log("o outside of f:", o);
console.log("p outside of f:", p);

Mutating signifie modifier certaines propriétés d'une Object existante. La copie de référence à laquelle une variable est liée et qui fait référence à cet objet reste la même. Les mutations sont donc visibles dans le champ de l'appelant.

Reassigning signifie remplacer la copie de référence liée à une variable. Comme il ne s'agit que d'une copie, les autres variables contenant une copie de la même référence ne sont pas affectées. Les réaffectations ne sont donc pas visibles dans le champ de l'appelant, contrairement à une stratégie d'évaluation appel par référence.

Informations complémentaires sur stratégies d'évaluation dans Ecmascript.

13
user6445533

JavaScript est transmis par la valeur . Pour les primitives, la valeur de la primitive est passée . Pour les objets, la référence "valeur" de l'objet est transmise.

Exemple avec Object:

var f1 = function(inputObject){
    inputObject.a=2;
}
var f2 = function(){
    var inputObject={"a":1};
    f1(inputObject); 
    console.log(inputObject.a);
}

l'appel de f2 entraîne l'affichage d'une valeur "a" égale à 2 au lieu de 1, lorsque la référence est transmise et que la valeur "a" dans la référence est mise à jour.

Exemple avec primitive:

var f1 = function(a){
    a=2;
}
var f2 = function(){
    var a =1;
    f1(a); 
    console.log(a);
}

l'appel de f2 entraîne l'affichage d'une valeur "1" sous la forme 1.

6
yallam

Concrètement, Alnitak est correct et facile à comprendre, mais finalement, en JavaScript, tout est passé en valeur. 

Quelle est la "valeur" d'un objet? C'est la référence de l'objet.

Lorsque vous transmettez un objet, vous obtenez une copie de cette valeur (d'où la "copie d'une référence" décrite par Alnitak). Si vous modifiez cette valeur, vous ne modifiez pas l'objet d'origine, vous modifiez votre copie de cette référence.

4
Pete Campbell

Dans l'intérêt de créer un exemple simple qui utilise const ...

const myRef = { foo: 'bar' };
const myVal = true;

function passes(r, v) {
  r.foo = 'baz';
  v = false;
}

passes(myRef, myVal);

console.log(myRef, myVal); // Object {foo: "baz"} true
4
4m1r

Les variables javascript "globales" sont des membres de l'objet window. Vous pouvez accéder à la référence en tant que membre de l'objet window.

var v = "initialized";
function byref(ref) {
 window[ref] = "changed by ref";
}
byref((function(){for(r in window){if(window[r]===v){return(r);}}})());
// could also be called like... byref('v');
console.log(v); // outputs changed by ref

Notez que l'exemple ci-dessus ne fonctionnera pas pour les variables déclarées dans une fonction.

3
tony41780

Sans purismes, je pense que la meilleure façon d’émuler un argument scalaire par référence en Javascript est d’utiliser un objet, comme le dit la réponse précédente.

Cependant, je fais un peu différent:

J'ai effectué l'assignation d'objet à l'intérieur de la fonction afin que les paramètres de référence soient visibles à proximité de l'appel de la fonction. Cela augmente la lisibilité de la source

Dans la déclaration de fonction, je mets les propriétés comme un commentaire, pour la même raison: la lisibilité.

var r;

funcWithRefScalars(r = {amount:200, message:null} );
console.log(r.amount + " - " + r.message);


function funcWithRefScalars(o) {  // o(amount, message)
  o.amount  *= 1.2;
  o.message = "20% increase";
}

Dans l'exemple ci-dessus, null indique clairement un paramètre de référence de sortie. 

La sortie:

240 - 20% Increase

Dans la partie client, console.log doit être remplacé par alert.

★ ★ ★ 

Une autre méthode qui peut être encore plus lisible:

var amount, message;

funcWithRefScalars(amount = [200], message = [null] );
console.log(amount[0] + " - " + message[0]);

function funcWithRefScalars(amount, message) {  // o(amount, message)
   amount[0]  *= 1.2;
   message[0] = "20% increase";
}

Ici, vous n'avez même pas besoin de créer de nouveaux noms factices, comme r ci-dessus. 

1
Paulo Buchsbaum

Les primitives sont passées par valeur. Mais si vous n'avez besoin que de lire la valeur d'une primitive (et que la valeur n'est pas connue au moment où la fonction est appelée), vous pouvez transmettre une fonction qui extrait la valeur au moment où vous en avez besoin.

function test(value) {
  console.log('retrieve value');
  console.log(value());
}

// call the function like this
var value = 1;
test(() => value);
0
jabko87

Les objets sont toujours passés par référence et les primitives par valeur, conservez simplement ce paramètre à la même adresse pour les objets. Voici un code illustrant ce que je veux dire (essayez-le dans un sandbox JavaScript tel que https://js.do/ ). Malheureusement, vous ne pouvez pas seulement conserver l'adresse du paramètre, vous conservez toutes les valeurs de membre d'origine. ainsi que.

a = { key: 'bevmo' };
testRetain(a);
document.write(' after function ');
document.write(a.key);


function testRetain (b)
 {
 document.write(' arg0 is ');
 document.write(arguments[0].key);
 b.key='passed by reference';
 var retain = b; //retaining the original address of the parameter

 //address of left set to address of right, changes address of parameter
 b={key: 'vons'}; //right is a new object with a new address
 document.write(' arg0 is ');
 document.write(arguments[0].key);

 //now retrieve the original address of the parameter for pass by reference
 b=retain; 
 document.write(' arg0 is ');
 document.write(arguments[0].key);
}

Résultat: arg0 est bevmo arg0 est vons arg0 est passé par référence après la fonction passée par référence

0
user3015682

Je ne vois pas référence par référence dans les exemples où des personnes tentent de démontrer cela. Je ne vois que valeur de passage

Dans le cas de variables qui contiennent une référence à un objet, la référence est le valeur de ces variables et, par conséquent, la référence est passée, qui est alors transmis par valeur

dans une déclaration comme celle-ci:

var a = {
  b:"foo", 
  c:"bar"
};

La valeur du 'a' n'est pas l'objet, mais la référence (jusqu'ici seulement) à cet objet. En d'autres termes, l'objet ne se trouve pas dans la variable a, il en fait référence. Je pense que cela semble difficile pour les programmeurs qui ne connaissent que JavaScript. Mais facile pour les personnes qui savent aussi par exemple Java, C # et C.

0
Juhani