web-dev-qa-db-fra.com

JavaScript est-il un langage de référence ou de valeur?

Les types primitifs (nombre, chaîne, etc.) sont passés par valeur, mais les objets sont inconnus, car ils peuvent être tous deux passés par valeur (dans le cas où nous considérons qu'une variable contenant un objet est en fait une référence à l'objet ) et passé par référence (quand on considère que la variable de l'objet contient l'objet lui-même).

Bien que cela ne compte pas vraiment à la fin, je veux savoir quelle est la bonne manière de présenter les arguments en passant des conventions. Existe-t-il un extrait de la spécification JavaScript qui définit quelle devrait être la sémantique à ce sujet?

1286
Danail Nachev

C'est intéressant en JavaScript. Considérons cet exemple:

function changeStuff(a, b, c)
{
  a = a * 10;
  b.item = "changed";
  c = {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);

Ceci produit la sortie:

10
changed
unchanged
  • Si obj1 n'était pas du tout une référence, alors modifier obj1.item n'aurait aucun effet sur le obj1 en dehors de la fonction.
  • Si l'argument était une référence appropriée, alors tout aurait changé. num serait 100, et obj2.item lirait "changed".

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

Concrètement, 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 le INTERNALS du paramètre, cela se propagera (comme avec obj1).

1505
deworde

C'est toujours passer par valeur, mais pour les objets, la valeur de la variable est une référence. De ce fait, lorsque vous passez un objet et modifiez ses membres, ces modifications persistent en dehors de la fonction. Cela rend look comme passer par référence. Mais si vous changez réellement la valeur de la variable d'objet, vous verrez que le changement ne persiste pas, prouvant qu'il est bien passé valeur par valeur.

Exemple:

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

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

var x = { member: "foo" };

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

console.log("before changeMember: " + x.member);
changeMember(x);
console.log("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
430
Tim Goodman

La variable ne "tient" pas l'objet; c'est une référence. Vous pouvez affecter cette référence à une autre variable. Désormais, tous deux référencent le même objet. C'est toujours passer par valeur (même si 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 JavaScript prenait en charge le passage par référence.

141
Shog9

Mes deux cents ... C'est la façon dont 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, peu importe que cela soit passé par valeur, par référence ou peu importe. 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 "affectation" ... Nous avons affecté 2 objets vides distincts aux variables "objet1" et "objet2".

Maintenant, disons que nous aimons mieux object1 ... Donc, nous "assignons" une nouvelle variable.

var favoriteObject = object1;

Ensuite, pour une raison quelconque, nous décidons que nous aimons mieux l’objet 2. Donc, nous faisons simplement une petite réaffectation.

favoriteObject = object2;

Rien n'est arrivé à object1 ou à object2. Nous n'avons pas changé de données du tout. Tout ce que nous avons fait a été de réaffecter ce que notre objet préféré est. Il est important de savoir que object2 et favoriteObject sont tous deux affectés au même objet. Nous pouvons changer 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, regardons maintenant les primitives comme les chaînes par exemple

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

Encore une fois, nous choisissons un favori.

var favoriteString = string1;

Nos variables favoriteString et string1 sont affectées à "Hello world". Maintenant, si nous voulons changer notre chaîne favorite ??? Qu'est-ce qui va arriver ???

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

Euh oh .... Qu'est-ce qui s'est passé. Nous ne pouvions pas changer de chaîne1 en changeant de chaîne favorite ... Pourquoi ?? Parce que nous n'avons pas changé notre objet string . Nous avons simplement fait "RE ASSIGN" la variable favoriteString dans une nouvelle chaîne. Cela l'a essentiellement déconnecté de string1. Dans l'exemple précédent, lorsque nous avons renommé notre objet, nous n'avons rien assigné. (Eh bien, pas à la variable elle-même , ... nous avons cependant affecté la propriété name à une nouvelle chaîne.) À la place, nous avons simplement muté l'objet qui garde les liens entre les 2 variables et les objets sous-jacents. (Même si nous avions voulu modifier ou muter l'objet chaîne lui-même , nous n’aurions pas pu, car les chaînes sont en réalité immuables en JavaScript.)

Passons maintenant aux fonctions et à la passation de paramètres .... Lorsque vous appelez une fonction et transmettez un paramètre, vous faites essentiellement une "affectation" à une nouvelle variable, et cela fonctionne exactement comme si vous affectiez simplement le signe égal (=).

Prenons 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';

    console.log(param1);   // Logs 'world'
}

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

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

OK, donnons maintenant quelques exemples utilisant des objets ... 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 the variable
otherObj = {
    firstName: 'Jack',
    lastName: 'Frost'
};

// Now, otherObj and myObject are assigned to 2 very different objects
// And mutating one object has no influence on 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 doesn't magically mutate 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 lisez tout ce billet, vous comprenez peut-être mieux comment les appels de fonctions fonctionnent en JavaScript. Peu importe que quelque chose soit passé par référence ou par valeur ... Ce qui compte, c'est l'affectation par rapport à la mutation.

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

Rappelez-vous toujours que le signe égal (=) signifie une affectation. Rappelez-vous toujours que transmettre un paramètre à une fonction en JavaScript signifie également une affectation. Elles sont identiques et les 2 variables sont connectées exactement de la même manière (c'est-à-dire qu'elles ne le sont pas, à moins que vous ne comptiez qu'elles sont affectées au même objet).

La seule fois que la "modification d'une variable" affecte une variable différente, c'est lorsque l'objet sous-jacent est muté (auquel cas vous n'avez pas modifié la variable, mais l'objet lui-même.

Il est inutile de faire une distinction entre les objets et les primitives, car cela fonctionne exactement comme si vous n'aviez pas de fonction et utilisiez simplement le signe égal pour affecter une nouvelle variable.

Le seul casse-tête est lorsque le nom de la variable que vous transmettez à la fonction est identique au nom du paramètre de la fonction. Lorsque cela se produit, vous devez traiter le paramètre dans la fonction comme s'il s'agissait d'une toute nouvelle variable privée de 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'
94
Ray Perea

Considérer ce qui suit:

  1. Les variables sont pointeurs aux valeurs en mémoire.
  2. Réaffecter une variable ne fait que pointer ce pointeur vers une nouvelle valeur.
  3. Réaffecter une variable n'affectera jamais les autres variables pointant vers le même objet.

Alors, oublier "passer par référence/valeur" ne vous attardez pas sur "passer par référence/valeur" car:

  1. Les termes ne sont utilisés que pour décrire le comportement d'un langage, pas nécessairement la mise en œuvre sous-jacente réelle. À la suite de cette abstraction, les détails essentiels qui sont essentiels pour une explication décente sont perdus, ce qui conduit inévitablement à la situation actuelle où un seul terme ne décrit pas correctement le comportement réel et où des informations supplémentaires doivent être fournies.
  2. Ces concepts n’étaient pas définis à l’origine dans le but de décrire javascript en particulier et je ne me sens donc pas obligé de les utiliser quand ils ne font qu’ajouter à la confusion.

Pour répondre à votre question: les pointeurs sont passés.


// code
var obj = {
    name: 'Fred',
    num: 1
};

// illustration
               'Fred'
              /
             /
(obj) ---- {}
             \
              \
               1


// code
obj.name = 'George';


// illustration
                 'Fred'


(obj) ---- {} ----- 'George'
             \
              \
               1


// code
obj = {};

// illustration
                 'Fred'


(obj)      {} ----- 'George'
  |          \
  |           \
 { }            1


// code
var obj = {
    text: 'Hello world!'
};

/* function parameters get their own pointer to 
 * the arguments that are passed in, just like any other variable */
someFunc(obj);


// illustration
(caller scope)        (someFunc scope)
           \             /
            \           /
             \         /
              \       /
               \     /
                 { }
                  |
                  |
                  |
            'Hello world'

Quelques derniers commentaires:

  • Il est tentant de penser que les primitives sont imposées par des règles spéciales tandis que les objets ne le sont pas, mais les primitives sont simplement la fin de la chaîne de pointeurs.
  • Comme dernier exemple, examinez pourquoi une tentative commune d'effacer un tableau ne fonctionne pas comme prévu.


var a = [1,2];
var b = a;

a = [];
console.log(b); // [1,2]
// doesn't work because `b` is still pointing at the original array
64
geg

Un objet en dehors d'une fonction est passé à une fonction en donnant une référence à l'objet extérieur.

Lorsque vous utilisez cette référence pour manipuler son objet, l'objet situé à l'extérieur est donc affecté. Cependant, si à l'intérieur de la fonction, vous décidiez de pointer la référence vers quelque chose d'autre, vous n'affecteriez pas l'objet du tout à l'extérieur, car tout ce que vous faisiez était de rediriger la référence vers autre chose.

23
user779764

Pensez-y comme ceci: c'est toujours passer par valeur. Cependant, la valeur d'un objet n'est pas l'objet lui-même, mais une référence à cet objet.

Voici un exemple, en passant un nombre (un type primitif)

function changePrimitive(val) {
    // At this point there are two '10's in memory.
    // Changing one won't affect the other
    val = val * 10;
}
var x = 10;
changePrimitive(x);
// x === 10

Répéter cela avec un objet donne des résultats différents:

function changeObject(obj) {
    // At this point there are two references (x and obj) in memory,
    // but these both point to the same object.
    // changing the object will change the underlying object that
    // x and obj both hold a reference to.
    obj.val = obj.val * 10;
}
var x = { val: 10 };
changeObject(x);
// x === { val: 100 }

Un autre exemple:

function changeObject(obj) {
    // Again there are two references (x and obj) in memory,
    // these both point to the same object.
    // now we create a completely new object and assign it.
    // obj's reference now points to the new object.
    // x's reference doesn't change.
    obj = { val: 100 };
}
var x = { val: 10 };
changeObject(x);
// x === { val: 10}
19
Phil Mander

JavaScript est toujours valeur de passage; tout est de type valeur.

Les objets sont des valeurs et les fonctions membres des objets sont des valeurs elles-mêmes (rappelez-vous que les fonctions sont des objets de première classe en JavaScript). Aussi, en ce qui concerne le concept que tout dans JavaScript est un objet; c'est faux. Les chaînes, symboles, nombres, booléens, nuls et indéfinis sont primitives.

À l'occasion, ils peuvent exploiter certaines fonctions et propriétés membres héritées de leurs prototypes de base, mais ce n'est que par commodité. Cela ne signifie pas qu'ils sont eux-mêmes des objets. Essayez ce qui suit pour référence:

x = "test";
alert(x.foo);
x.foo = 12;
alert(x.foo);

Dans les deux alertes, vous constaterez que la valeur n'est pas définie.

14
Michael Roberts

Une explication très détaillée sur la copie, la transmission et la comparaison par valeur et par référence se trouve dans ce chapitre du "JavaScript: le guide définitif" livre.

Avant de quitter le sujet de la manipulation d'objets et de tableaux par référence, nous devons clarifier un point de la nomenclature.

L'expression "passer par référence" peut avoir plusieurs significations. Pour certains lecteurs, l'expression fait référence à une technique d'appel de fonction qui permet à une fonction d'attribuer de nouvelles valeurs à ses arguments et de faire en sorte que les valeurs modifiées soient visibles à l'extérieur de la fonction. Ce n'est pas la façon dont le terme est utilisé dans ce livre.

Ici, nous entendons simplement qu'une référence à un objet ou à un tableau - et non à l'objet lui-même - est transmise à une fonction. Une fonction peut utiliser la référence pour modifier les propriétés de l'objet ou des éléments du tableau. Mais si la fonction remplace la référence par une référence à un nouvel objet ou à un nouveau tableau, cette modification n'est pas visible en dehors de la fonction.

Les lecteurs familiarisés avec l’autre sens de ce terme peuvent préférer dire que les objets et les tableaux sont passés par valeur, mais que la valeur transmise est en réalité une référence plutôt que l’objet lui-même.

13
igor

En JavaScript, le type de la valeur uniquement détermine si cette valeur sera affectée par value-copy ou par référence-copie .

Les valeurs primitives sont toujours attribuées/passées par value-copy :

  • null
  • undefined
  • chaîne
  • nombre
  • booléen
  • symbole dans ES6

Les valeurs composées sont toujours assignées/passées par reference-copy

  • objets
  • tableaux
  • une fonction

Par exemple

var a = 2;
var b = a; // `b` is always a copy of the value in `a`
b++;
a; // 2
b; // 3

var c = [1,2,3];
var d = c; // `d` is a reference to the shared `[1,2,3]` value
d.Push( 4 );
c; // [1,2,3,4]
d; // [1,2,3,4]

Dans l'extrait de code ci-dessus, étant donné que 2 est une primitive scalaire, a contient une copie initiale de cette valeur et b se voit attribuer une autre copie de la valeur. Lorsque vous modifiez b, vous ne modifiez en aucun cas la valeur dans a.

Mais c et d sont tous deux des références distinctes à la même valeur partagée [1,2,3], qui est une valeur composée. Il est important de noter que ni c ni d more ne "possèdent" la valeur [1,2,3] - les deux ne sont que des références homologues égales à la valeur. Ainsi, lorsque vous utilisez l'une ou l'autre référence pour modifier (.Push(4)) la valeur partagée array réelle elle-même, elle affecte uniquement la valeur partagée partagée et les deux références font référence à la valeur nouvellement modifiée [1,2,3,4].

var a = [1,2,3];
var b = a;
a; // [1,2,3]
b; // [1,2,3]

// later
b = [4,5,6];
a; // [1,2,3]
b; // [4,5,6]

Lorsque nous faisons l'affectation b = [4,5,6], nous ne faisons absolument rien pour affecter où a fait toujours référence ([1,2,3]). Pour faire cela, b devrait être un pointeur sur a plutôt qu'une référence à la array - mais une telle capacité n'existe pas dans JS!

function foo(x) {
    x.Push( 4 );
    x; // [1,2,3,4]

    // later
    x = [4,5,6];
    x.Push( 7 );
    x; // [4,5,6,7]
}

var a = [1,2,3];

foo( a );

a; // [1,2,3,4]  not  [4,5,6,7]

Lorsque nous passons dans l'argument a, il attribue une copie de la référence a à x. x et a sont des références distinctes pointant sur la même valeur [1,2,3]. Maintenant, dans la fonction, nous pouvons utiliser cette référence pour muter la valeur elle-même (Push(4)). Mais lorsque nous faisons l'affectation x = [4,5,6], cela n'affecte en aucun cas l'emplacement de la référence initiale a - pointe toujours sur la valeur (maintenant modifiée) [1,2,3,4].

Pour transmettre efficacement une valeur composée (telle qu'une array) par valeur-copie, vous devez en faire une copie manuellement, de sorte que la référence transmise ne pointe pas toujours vers l'original. Par exemple:

foo( a.slice() );

Valeur composée (objet, tableau, etc.) pouvant être transmise par référence-copie

function foo(wrapper) {
    wrapper.a = 42;
}

var obj = {
    a: 2
};

foo( obj );

obj.a; // 42

Ici, obj agit comme un wrapper pour la propriété primitive scalaire a. Lorsque passé à foo(..), une copie de la référence obj est transmise et définie sur le paramètre wrapper. Nous pouvons maintenant utiliser la référence wrapper pour accéder à l'objet partagé et mettre à jour sa propriété. Une fois la fonction terminée, obj.a verra la valeur mise à jour 42.

Source

12
zangw

JavaScript transmet les types primitifs par valeur et les types d'objet par référence

Maintenant, les gens aiment se disputer sans cesse pour savoir si "passer par référence" est la bonne façon de décrire ce que Java et al. fait réellement. Le point est la suivante:

  1. Passer un objet ne copie pas l'objet.
  2. Un membre passé à une fonction peut avoir ses membres modifiés par la fonction.
  3. Une valeur primitive transmise à une fonction ne peut pas être modifiée par la fonction. Une copie est faite.

Dans mon livre, cela s'appelle passer par référence.

- Brian Bi - Quels langages de programmation passent par référence?


Mise à jour

Voici une réfutation à ceci:

Il n'y a pas de "référence par référence" disponible en JavaScript.

6
georgeawg

C'est peu plus d'explication pour passer par valeur et passer par référence (JavaScript). Dans ce concept, ils parlent de passer la variable par référence et de passer par la variable.

Pass par valeur (type primitif)

var a = 3;
var b = a;

console.log(a); // a = 3
console.log(b); // b = 3

a=4;
console.log(a); // a = 4
console.log(b); // b = 3
  • s'applique à tous les types primitifs en JavaScript (chaîne, nombre, booléen, indéfini et null).
  • une mémoire est allouée à a (par exemple 0x001) et b crée une copie de la valeur en mémoire (par exemple 0x002).
  • Donc, changer la valeur d'une variable n'affecte pas l'autre, car elles résident toutes deux à deux endroits différents.

Pass par référence (objets)

var c = { "name" : "john" };
var d = c;

console.log(c); // { "name" : "john" }
console.log(d); // { "name" : "john" }

c.name = "doe";

console.log(c); // { "name" : "doe" }
console.log(d); // { "name" : "doe" }
  • Le moteur JavaScript assigne l'objet à la variable c et pointe vers de la mémoire, par exemple (0x012).
  • Lorsque d = c, à cette étape, d pointe vers le même emplacement (0x012).
  • Changer la valeur de toute valeur de changement pour la variable.
  • Les fonctions sont des objets

Cas particulier, passer par référence (objets)

c = {"name" : "jane"};
console.log(c); // { "name" : "jane" }
console.log(d); // { "name" : "doe" }
  • L'opérateur égal (=) définit un nouvel espace mémoire ou une nouvelle adresse
5
Ashish Singh Rawat

partager ce que je sais des références en JavaScript

En JavaScript, objets sont stockés en tant que références:

var a = {
  a: 1,
  b: 2,
  c: 3
};
var b = a;

// b.c is referencing to a.c value
console.log(b.c) // Output: 3
// Changing value of b.c
b.c = 4
// Also changes the value of a.c
console.log(a.c) // Output: 4
4
student

Sémantique!! Définir des définitions concrètes rendra nécessairement certaines réponses et certains commentaires incompatibles, car ils ne décrivent pas la même chose, même en utilisant les mêmes mots et expressions, mais il est essentiel de surmonter la confusion (surtout pour les nouveaux programmeurs).

Tout d’abord, il existe de nombreux niveaux d’abstraction que tout le monde ne semble pas comprendre. Les nouveaux programmeurs qui ont appris la 4ème ou la 5ème génération de langages peuvent avoir du mal à comprendre les concepts bien connus des programmeurs Assembly ou C, non pointés par des pointeurs en pointeurs. Pass-by-reference ne signifie pas simplement la possibilité de modifier un objet référencé à l'aide d'une variable de paramètre de fonction.

Variable: Concept combiné d'un symbole qui référence une valeur à un emplacement particulier de la mémoire. Ce terme est généralement trop chargé pour être utilisé seul dans les détails.

Symbol: Chaîne de texte utilisée pour désigner une variable (c'est-à-dire le nom de la variable).

Value: Bits particuliers stockés en mémoire et référencés à l'aide du symbole de la variable.

Emplacement mémoire: Où la valeur d'une variable est stockée. (L'emplacement lui-même est représenté par un nombre distinct de la valeur stockée dans l'emplacement.)

paramètre de fonction: Variable déclarée dans une définition de fonction, utilisée pour référencer les variables transmises à la fonction.

argument de la fonction: Variable en dehors de la fonction transmise à la fonction par l'appelant.

Variable d'objet: Variable dont la valeur sous-jacente de base n'est pas "l'objet" lui-même, mais sa valeur est un pointeur (valeur d'emplacement de mémoire) vers un autre emplacement en mémoire où les données réelles de l'objet sont stockées. Dans la plupart des langues de génération supérieure, l'aspect "pointeur" est efficacement masqué par le dé-référencement automatique dans divers contextes.

Variable primitive: Variable dont la valeur IS la valeur réelle. Même ce concept peut être compliqué par la boxe automatique et les contextes de plusieurs langages semblables à des objets, mais l'idée générale est que la valeur de la variable IS la valeur réelle représentée par le symbole de la variable plutôt qu'un pointeur vers une autre mémoire. emplacement.

Les arguments et paramètres de fonction ne sont pas la même chose. De plus, la valeur d'une variable n'est pas l'objet de la variable (comme déjà souligné par diverses personnes, mais apparemment ignoré). Ces distinctions sont essentielles à une bonne compréhension.

Pass-by-value ou Call-by-sharing (pour les objets): la valeur de l'argument de la fonction est COPIÉE dans un autre emplacement de mémoire référencé par le symbole de paramètre de la fonction (qu'il soit sur la pile ou tas). En d'autres termes, le paramètre de fonction a reçu une copie de la valeur de l'argument passé ... ET (critique) de la valeur de l'argument IS JAMAIS MISE À JOUR/MODIFIÉE/MODIFIÉE par la fonction appelante. N'oubliez pas que la valeur d'une variable d'objet n'est PAS l'objet lui-même, mais bien le pointeur sur l'objet. Par conséquent, le fait de passer d'une variable d'objet à une valeur copie le pointeur vers la variable de paramètre de fonction. La valeur du paramètre de fonction pointe sur le même objet en mémoire. Les données d'objet elles-mêmes peuvent être modifiées directement via le paramètre de fonction, MAIS la valeur de l'argument de fonction IS NE JAMAIS MISE À JOUR, de sorte qu'il continuera de pointer sur le identique objet pendant et même après l'appel de la fonction (même si les données de son objet ont été modifiées ou si un paramètre différent a été attribué au paramètre de fonction). Il est incorrect de conclure que l'argument de la fonction a été passé par référence simplement parce que l'objet référencé peut être mis à jour via la variable de paramètre de la fonction.

Call/Pass-by-reference: La valeur de l'argument de la fonction peut/sera mise à jour directement par le paramètre de la fonction correspondante. Si cela aide, le paramètre de fonction devient un "alias" efficace pour l'argument - ils font en réalité référence à la même valeur au même emplacement mémoire. Si un argument de fonction est une variable d'objet, la possibilité de modifier les données de l'objet n'est pas différente de la casse passage par valeur puisque le paramètre de fonction pointe toujours sur le même objet que l'argument. Mais dans le cas de la variable d'objet, si le paramètre de fonction est défini sur un objet complètement différent, l'argument pointe également de la même manière sur l'objet différent - cela ne se produit pas dans le cas du passage par valeur.

JavaScript ne passe pas par référence. Si vous lisez attentivement, vous vous rendrez compte que toutes les opinions contraires comprennent mal ce que l'on entend par valeur de passage et qu'ils concluent à tort que la possibilité de mettre à jour les données d'un objet via le paramètre de fonction est synonyme de "valeur de passage".

Object clone/copy: Un nouvel objet est créé et les données de l'objet d'origine sont copiées. Cela peut être une copie profonde ou une copie superficielle, mais le fait est qu'un nouvel objet est créé. La création d'une copie d'un objet est un concept distinct de la définition par valeur. Certains langages font la distinction entre un objet de classe et des structures (ou similaires) et peuvent avoir un comportement différent en ce qui concerne le passage de variables de types différents. Mais JavaScript ne fait rien automatiquement de la sorte lors de la transmission de variables d'objet. Mais l’absence de clonage automatique d’objets ne se traduit pas par un renvoi par référence.

3
C Perkins

Passer des arguments à une fonction en JavaScript est analogue à passer des paramètres par la valeur du pointeur en C:

/*
The following C program demonstrates how arguments
to JavaScript functions are passed in a way analogous
to pass-by-pointer-value in C. The original JavaScript
test case by @Shog9 follows with the translation of
the code into C. This should make things clear to
those transitioning from C to JavaScript.

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

This produces the output:

10
changed
unchanged
*/

#include <stdio.h>
#include <stdlib.h>

struct obj {
    char *item;
};

void changeStuff(int *num, struct obj *obj1, struct obj *obj2)
{
    // make pointer point to a new memory location
    // holding the new integer value
    int *old_num = num;
    num = malloc(sizeof(int));
    *num = *old_num * 10;
    // make property of structure pointed to by pointer
    // point to the new value
    obj1->item = "changed";
    // make pointer point to a new memory location
    // holding the new structure value
    obj2 = malloc(sizeof(struct obj));
    obj2->item = "changed";
    free(num); // end of scope
    free(obj2); // end of scope
}

int num = 10;
struct obj obj1 = { "unchanged" };
struct obj obj2 = { "unchanged" };

int main()
{
    // pass pointers by value: the pointers
    // will be copied into the argument list
    // of the called function and the copied
    // pointers will point to the same values
    // as the original pointers
    changeStuff(&num, &obj1, &obj2);
    printf("%d\n", num);
    puts(obj1.item);
    puts(obj2.item);
    return 0;
}
3
John Sonderson

Mon moyen simple de comprendre cela ...

  • Lorsque vous appelez une fonction, vous transmettez le contenu (référence ou valeur) des variables d’argument, pas les variables elles-mêmes.

    var var1 = 13;
    var var2 = { prop: 2 };
    
    //13 and var2's content (reference) are being passed here
    foo(var1, var2); 
    
  • Dans la fonction, les variables de paramètre inVar1 et inVar2 reçoivent le contenu transmis.

    function foo(inVar1, inVar2){
        //changing contents of inVar1 and inVar2 won't affect variables outside
        inVar1 = 20;
        inVar2 = { prop: 7 };
    }
    
  • Puisque inVar2 a reçu la référence de { prop: 2 }, vous pouvez modifier la valeur de la propriété de l'objet.

    function foo(inVar1, inVar2){
        inVar2.prop = 7; 
    }
    
2
dpp

Pour les juristes spécialistes du langage de programmation, j'ai parcouru les sections suivantes de ECMAScript 5.1 (qui est plus facile à lire que la dernière édition) et je vais aussi loin que demander sur la liste de diffusion ECMAScript.

TL; DR: Tous les éléments sont passés par valeur, mais les propriétés des objets sont des références et la définition de Object manque cruellement de norme.

Construction de listes d'arguments

La Section 11.2.4 "Listes d'arguments" dit ce qui suit pour produire une liste d'arguments composée d'un seul argument:

La production ArgumentList: AssignmentExpression est évaluée comme suit:

  1. Soit ref le résultat de l'évaluation de AssignmentExpression.
  2. Laissez arg être GetValue (ref).
  3. Renvoie une liste dont le seul élément est arg.

La section énumère également les cas où la liste d'arguments a 0 ou> 1 arguments.

Ainsi, tout est passé par référence.

Accès aux propriétés de l'objet

Section 11.2.1 "Accesseurs de propriété"

La production MemberExpression: MemberExpression [Expression] est évaluée comme suit:

  1. Laissez baseReference le résultat de l’évaluation de MemberExpression.
  2. Laissez baseValue être GetValue (baseReference).
  3. Soit propertyNameReference le résultat de l'évaluation de l'expression.
  4. Laissez propertyNameValue être GetValue (propertyNameReference).
  5. Appelez CheckObjectCoercible (baseValue).
  6. Soit propertyNameString To ToString (propertyNameValue).
  7. Si la production syntaxique en cours d'évaluation est contenue dans du code en mode strict, laissez strict la valeur true, sinon strict la valeur false.
  8. Retourne une valeur de type référence dont la valeur de base est baseValue et dont le nom référencé est propertyNameString, et dont l'indicateur de mode strict est strict.

Ainsi, les propriétés des objets sont toujours disponibles comme référence.

Sur référence

Il est décrit dans la section 8.7 "Le type de spécification de référence", que les références ne sont pas des types réels dans le langage. Elles sont uniquement utilisées pour décrire le comportement des opérateurs de suppression, de typeof et d'affectation.

Définition de "objet"

Il est défini dans l'édition 5.1 que "Un objet est une collection de propriétés". Par conséquent, nous pouvons en déduire que la valeur de l'objet est la collection, mais en ce qui concerne la valeur de la collection est mal définie dans la spécification et nécessite un peu de effort pour la comprendre.

2
DannyNiu

Il y a quelques discussions sur l'utilisation du terme "passer par référence" en JavaScript ici , mais pour répondre à votre question:

Un objet est automatiquement passé par référence, sans qu'il soit nécessaire de l'indiquer spécifiquement

(Extrait de l'article mentionné ci-dessus.)

2
Jack Sleight

Dans un langage de bas niveau, si vous voulez passer une variable par référence, vous devez utiliser une syntaxe spécifique lors de la création de la fonction:

int myAge = 14;
increaseAgeByRef(myAge);
function increaseAgeByRef(int &age) {
  *age = *age + 1;
}

Le &age est une référence à myAge, mais si vous souhaitez obtenir la valeur, vous devez convertir la référence à l'aide de *age.

Javascript est un langage de haut niveau qui effectue cette conversion pour vous. Ainsi, bien que les objets soient passés par référence, le langage convertit le paramètre de référence en valeur. Vous n'avez pas besoin d'utiliser &, dans la définition de la fonction pour la transmettre par référence, ni *, dans le corps de la fonction, pour convertir la référence en valeur, JS le fait pour vous.

C’est pourquoi, lorsque vous essayez de modifier un objet dans une fonction en remplaçant sa valeur (c.-à-d. age = {value:5}), le changement ne persiste pas. Si vous modifiez ses propriétés (c.-à-d. age.value = 5), .

en savoir plus

2
Narayon

J'ai lu ces réponses plusieurs fois, mais je ne l'ai VRAIMENT pas compris jusqu'à ce que je connaisse la définition technique de "Appeler en partageant" , comme l'a dit Barbara Liskov.

La sémantique de l'appel par partage diffère de l'appel par référence en ce que les assignations aux arguments de fonction dans la fonction ne sont pas visibles pour l'appelant (contrairement à la sémantique de référence) [citation nécessaire], donc par ex. si une variable a été transmise, il n'est pas possible de simuler une affectation sur cette variable dans la portée de l'appelant. Cependant, comme la fonction a accès au même objet que l'appelant (aucune copie n'est faite), les mutations de ces objets, si les objets sont mutables, sont visibles pour l'appelant, ce qui peut sembler différer de l'appel par la valeur. sémantique. Les mutations d'un objet mutable au sein de la fonction sont visibles pour l'appelant car l'objet n'est ni copié ni copié - il est partagé.

Autrement dit, les références de paramètre sont modifiables si vous accédez à la valeur du paramètre elle-même. Par ailleurs, l’affectation à un paramètre disparaît après l’évaluation et n’est pas accessible à l’appelant de la fonction.

1
steviejay

Les médecins de MDN l'expliquent clairement, sans être trop verbeux:

Les paramètres d'un appel de fonction sont les arguments de la fonction . Les arguments sont passés aux fonctions par valeur . Si la fonction modifie la valeur d'un argument, cette modification n'est pas reflétée globalement ni dans la fonction appelante. Cependant, les références d'objet sont aussi des valeurs, et elles sont spéciales: si la fonction modifie les propriétés de l'objet référé, cette modification est visible en dehors de la fonction, (...)

Source: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions#Description

1
miguelr

L’explication la plus succincte que j’ai trouvée se trouve dans le guide de style AirBNB :

  • Primitives : Lorsque vous accédez à un type primitif, vous travaillez directement sur sa valeur.

    • chaîne
    • nombre
    • booléen
    • nul
    • undefined

Par exemple.:

var foo = 1,
    bar = foo;

bar = 9;

console.log(foo, bar); // => 1, 9
  • Complex : Lorsque vous accédez à un type complexe, vous travaillez sur une référence à sa valeur.

    • objet
    • tableau
    • une fonction

Par exemple.:

var foo = [1, 2],
    bar = foo;

bar[0] = 9;

console.log(foo[0], bar[0]); // => 9, 9

C'est à dire. effectivement, les types primitifs sont passés par valeur et les types complexes par référence.

1
lid

Un moyen facile de déterminer si un élément est "passer par référence" est de savoir si vous pouvez écrire une fonction "swap". Par exemple, en C, vous pouvez faire:

void swap(int *i, int *j)
{
    int t;
    t = *i;
    *i = *j;
    *j = t;
}

Si vous ne pouvez pas faire l'équivalent de cela en JavaScript, ce n'est pas "passer par référence".

0
Ross

J'ai trouvé la méthode extend de la bibliothèque Underscore.js très utile lorsque je souhaite passer un objet en tant que paramètre pouvant être modifié ou remplacé intégralement.

function replaceOrModify(aObj) {
  if (modify) {

    aObj.setNewValue('foo');

  } else {

   var newObj = new MyObject();
   // _.extend(destination, *sources) 
   _.extend(newObj, aObj);
  }
}
0
Jack
  1. Les primitives (nombre, booléen, etc.) sont passées par valeur.
    • Les chaînes sont immuables, donc cela ne les concerne pas vraiment.
  2. Les objets sont passés par référence (la référence est passée par valeur).
0
uriz

Je dirais que c'est passe-à-copie -

Les arguments à considérer et les objets variables sont des objets créés au cours du contexte d’exécution créé au début de l’appel de la fonction - et votre valeur/référence réelle transmise à la fonction est simplement stockée dans cet objet arguments + variables.

En termes simples, pour les types primitifs, les valeurs sont copiées au début de l'appel de fonction, pour le type d'objet, la référence est copiée.

0
lyslim