JavaScript è un linguaggio pass-by-reference o pass-by-value?

Feb 06 2009

I tipi primitivi (numero, stringa, ecc.) Vengono passati per valore, ma gli oggetti sono sconosciuti, perché possono essere entrambi passati per valore (nel caso si consideri che una variabile che contiene un oggetto è in realtà un riferimento all'oggetto ) e passato per riferimento (se si considera che la variabile dell'oggetto contiene l'oggetto stesso).

Anche se alla fine non ha molta importanza, voglio sapere qual è il modo corretto di presentare gli argomenti che passano le convenzioni. C'è un estratto dalla specifica JavaScript, che definisce quale dovrebbe essere la semantica riguardo a questo?

Risposte

1665 deworde Sep 04 2010 at 00:17

È interessante in JavaScript. Considera questo esempio:

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

Questo produce l'output:

10
changed
unchanged
  • Se obj1non fosse affatto un riferimento, la modifica non obj1.itemavrebbe alcun effetto obj1all'esterno della funzione.
  • Se l'argomento fosse un riferimento appropriato, allora tutto sarebbe cambiato. numsarebbe 100, e obj2.itemleggerebbe "changed".

Invece, la situazione è che l'elemento passato viene passato per valore. Ma l'elemento passato per valore è esso stesso un riferimento. Tecnicamente, questo è chiamato call-by-sharing .

In termini pratici, ciò significa che se modifichi il parametro stesso (come con nume obj2), ciò non influirà sull'elemento che è stato inserito nel parametro. Ma se modifichi gli INTERNI del parametro, ciò si propagherà di nuovo (come con obj1).

493 TimGoodman Mar 15 2011 at 23:38

È sempre passato per valore, ma per gli oggetti il ​​valore della variabile è un riferimento. Per questo motivo, quando si passa un oggetto e si modificano i suoi membri , tali modifiche persistono al di fuori della funzione. Questo lo fa sembrare un riferimento. Ma se effettivamente modifichi il valore della variabile oggetto vedrai che la modifica non persiste, dimostrando che è davvero passata per valore.

Esempio:

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 */

Produzione:

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

before changeMember: foo
in changeMember: bar
after changeMember: bar
156 Shog9 Feb 06 2009 at 04:37

La variabile non "trattiene" l'oggetto; contiene un riferimento. È possibile assegnare quel riferimento a un'altra variabile e ora entrambi fanno riferimento allo stesso oggetto. È sempre passato per valore (anche quando quel valore è un riferimento ...).

Non c'è modo di alterare il valore detenuto da una variabile passata come parametro, cosa che sarebbe possibile se JavaScript supportasse il passaggio per riferimento.

115 RayPerea Aug 04 2014 at 18:06

I miei due centesimi ... Questo è il modo in cui lo intendo. (Sentiti libero di correggermi se sbaglio)

È ora di buttare via tutto ciò che sai sul passaggio per valore / riferimento.

Perché in JavaScript, non importa se viene passato per valore o per riferimento o altro. Ciò che conta è la mutazione vs l'assegnazione dei parametri passati in una funzione.

OK, fammi fare del mio meglio per spiegare cosa intendo. Supponiamo che tu abbia alcuni oggetti.

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

Quello che abbiamo fatto è "assegnazione" ... Abbiamo assegnato 2 oggetti vuoti separati alle variabili "oggetto1" e "oggetto2".

Ora, diciamo che ci piace di più object1 ... Quindi, "assegniamo" una nuova variabile.

var favoriteObject = object1;

Successivamente, per qualsiasi motivo, decidiamo che ci piace di più l'oggetto 2. Quindi, facciamo un piccolo riassegnazione.

favoriteObject = object2;

Non è successo niente a object1 o object2. Non abbiamo modificato alcun dato. Tutto ciò che abbiamo fatto è stato riassegnare il nostro oggetto preferito. È importante sapere che object2 e favouriteObject sono entrambi assegnati allo stesso oggetto. Possiamo cambiare quell'oggetto tramite una di queste variabili.

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

OK, ora diamo un'occhiata alle primitive come le stringhe, ad esempio

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

Ancora una volta, scegliamo un preferito.

var favoriteString = string1;

Entrambe le nostre variabili favoriteString e string1 sono assegnate a "Hello world". E se volessimo cambiare la nostra stringa preferita ??? Cosa accadrà???

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

Uh oh ... Cos'è successo. Non è stato possibile modificare string1 modificando favoriteString ... Perché ?? Perché non abbiamo cambiato il nostro oggetto stringa . Tutto quello che abbiamo fatto è stato "RE ASSIGN" la variabile favouriteString a una nuova stringa. Questo essenzialmente lo ha disconnesso da string1. Nell'esempio precedente, quando abbiamo rinominato il nostro oggetto, non abbiamo assegnato nulla. (Beh, non alla variabile stessa , ... abbiamo tuttavia assegnato la proprietà name a una nuova stringa.) Invece, abbiamo mutato l'oggetto che mantiene le connessioni tra le 2 variabili e gli oggetti sottostanti. (Anche se avessimo voluto modificare o mutare l'oggetto stringa stesso , non avremmo potuto farlo, perché le stringhe sono effettivamente immutabili in JavaScript.)

Ora, passiamo alle funzioni e al passaggio di parametri ... Quando chiami una funzione e passi un parametro, ciò che stai essenzialmente facendo è un "assegnamento" a una nuova variabile, e funziona esattamente come se lo avessi assegnato usando il segno di uguale (=).

Prendi questi esempi.

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'

Ora, la stessa cosa, ma con una funzione

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, ora diamo alcuni esempi usando gli oggetti invece ... prima, senza la funzione.

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

Ora, la stessa cosa, ma con una chiamata di funzione

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, se leggi l'intero post, forse ora hai una migliore comprensione di come funzionano le chiamate di funzione in JavaScript. Non importa se qualcosa viene passato per riferimento o per valore ... Ciò che conta è l'assegnazione o la mutazione.

Ogni volta che si passa una variabile a una funzione, si "Assegna" a qualunque sia il nome della variabile parametro, proprio come se si usasse il segno di uguale (=).

Ricorda sempre che il segno di uguale (=) significa assegnazione. Ricorda sempre che passare un parametro a una funzione in JavaScript significa anche assegnazione. Sono uguali e le 2 variabili sono collegate esattamente allo stesso modo (vale a dire non lo sono, a meno che non si conti che siano assegnate allo stesso oggetto).

L'unica volta in cui "la modifica di una variabile" influisce su una variabile diversa è quando l'oggetto sottostante viene mutato (nel qual caso non hai modificato la variabile, ma l'oggetto stesso.

Non ha senso fare una distinzione tra oggetti e primitive, perché funziona allo stesso modo come se non avessi una funzione e usassi solo il segno di uguale per assegnare una nuova variabile.

L'unico problema è quando il nome della variabile che passi alla funzione è lo stesso del nome del parametro della funzione. Quando ciò accade, devi trattare il parametro all'interno della funzione come se fosse una variabile completamente nuova privata della funzione (perché lo è)

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'
75 geg Sep 26 2015 at 11:06

Considera quanto segue:

  1. Le variabili sono puntatori a valori in memoria.
  2. Riassegnare una variabile punta semplicemente quel puntatore a un nuovo valore.
  3. La riassegnazione di una variabile non influirà mai sulle altre variabili che puntavano a quello stesso oggetto

Quindi, dimentica "passa per riferimento / valore" non rimanere bloccato su "passa per riferimento / valore" perché:

  1. I termini sono usati solo per descrivere il comportamento di un linguaggio, non necessariamente l'effettiva implementazione sottostante. Come risultato di questa astrazione, i dettagli critici che sono essenziali per una spiegazione decente vengono persi, il che porta inevitabilmente alla situazione attuale in cui un singolo termine non descrive adeguatamente il comportamento effettivo e devono essere fornite informazioni supplementari
  2. Questi concetti non sono stati originariamente definiti con l'intento di descrivere javascript in particolare e quindi non mi sento obbligato a usarli quando aggiungono solo confusione.

Per rispondere alla tua domanda: vengono passati i puntatori.


// 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'

Alcuni commenti finali:

  • Si è tentati di pensare che le primitive siano applicate da regole speciali mentre gli oggetti non lo sono, ma le primitive sono semplicemente la fine della catena del puntatore.
  • Come ultimo esempio, considera perché un tentativo comune di cancellare un array non funziona come previsto.


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
24 user779764 Jun 03 2011 at 06:04

Un oggetto esterno a una funzione viene passato in una funzione fornendo un riferimento all'oggetto esterno.

Quando si utilizza quel riferimento per manipolare il suo oggetto, l'oggetto all'esterno ne viene influenzato. Tuttavia, se all'interno della funzione hai deciso di puntare il riferimento a qualcos'altro, non hai influenzato affatto l'oggetto all'esterno, perché tutto ciò che hai fatto è stato reindirizzare il riferimento a qualcos'altro.

22 PhilMander Feb 13 2015 at 18:00

Pensala in questo modo: è sempre passato per valore. Tuttavia, il valore di un oggetto non è l'oggetto stesso, ma un riferimento a quell'oggetto.

Ecco un esempio, passando un numero (un tipo primitivo)

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

Ripetendolo con un oggetto si ottengono risultati diversi:

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 altro esempio:

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}
20 igor May 13 2011 at 05:20

Una spiegazione molto dettagliata su come copiare, passare e confrontare per valore e per riferimento si trova in questo capitolo del libro "JavaScript: The Definitive Guide" .

Prima di abbandonare l'argomento della manipolazione di oggetti e array per riferimento, è necessario chiarire un punto di nomenclatura.

La frase "passa per riferimento" può avere diversi significati. Per alcuni lettori, la frase si riferisce a una tecnica di invocazione di funzioni che consente a una funzione di assegnare nuovi valori ai propri argomenti e di avere quei valori modificati visibili all'esterno della funzione. Questo non è il modo in cui il termine è usato in questo libro.

Qui intendiamo semplicemente che un riferimento a un oggetto oa un array, non all'oggetto stesso, viene passato a una funzione. Una funzione può utilizzare il riferimento per modificare le proprietà dell'oggetto o degli elementi dell'array. Ma se la funzione sovrascrive il riferimento con un riferimento a un nuovo oggetto o matrice, tale modifica non è visibile al di fuori della funzione.

I lettori che hanno familiarità con l'altro significato di questo termine potrebbero preferire dire che gli oggetti e gli array vengono passati per valore, ma il valore passato è in realtà un riferimento piuttosto che l'oggetto stesso.

18 MichaelRoberts Feb 06 2009 at 04:49

JavaScript è sempre un valore di passaggio ; tutto è di tipo valore.

Gli oggetti sono valori e le funzioni membro degli oggetti sono valori stessi (ricorda che le funzioni sono oggetti di prima classe in JavaScript). Inoltre, per quanto riguarda il concetto che tutto in JavaScript è un oggetto ; questo è sbagliato. Stringhe, simboli, numeri, valori booleani, null e undefined sono primitivi .

A volte possono sfruttare alcune funzioni e proprietà dei membri ereditate dai loro prototipi di base, ma questo è solo per comodità. Non significa che siano oggetti stessi. Prova quanto segue per riferimento:

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

In entrambi gli avvisi troverai il valore non definito.

13 zangw Dec 30 2015 at 09:20

In JavaScript, il tipo di valore controlla esclusivamente se quel valore verrà assegnato da value-copy o da reference-copy .

I valori primitivi vengono sempre assegnati / passati da value-copy :

  • null
  • undefined
  • corda
  • numero
  • booleano
  • simbolo in ES6

I valori composti vengono sempre assegnati / passati dalla copia di riferimento

  • oggetti
  • array
  • funzione

Per esempio

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]

Nello snippet precedente, poiché 2è una primitiva scalare, acontiene una copia iniziale di quel valore e bviene assegnata un'altra copia del valore. Quando si modifica b, non si modifica in alcun modo il valore in a.

Ma entrambi ce dsono riferimenti separati allo stesso valore condiviso [1,2,3], che è un valore composto. È importante notare che né cdpiù "possiede" il [1,2,3]valore: entrambi sono solo riferimenti uguali al valore. Quindi, quando si utilizza uno dei riferimenti per modificare ( .push(4)) il arrayvalore condiviso effettivo stesso, influisce solo sull'unico valore condiviso ed entrambi i riferimenti faranno riferimento al valore appena modificato [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]

Quando eseguiamo l'assegnazione b = [4,5,6], non stiamo facendo assolutamente nulla per influenzare il punto in cui afa ancora riferimento ( [1,2,3]). Per fare ciò, bdovrebbe essere un puntatore a apiuttosto che un riferimento a array- ma tale capacità non esiste in 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]

Quando passiamo l'argomento a, assegna una copia del ariferimento a x. xe asono riferimenti separati che puntano allo stesso [1,2,3]valore. Ora, all'interno della funzione, possiamo usare quel riferimento per mutare il valore stesso ( push(4)). Ma quando eseguiamo l'assegnazione x = [4,5,6], ciò non influisce in alcun modo sul punto in cui apunta il riferimento iniziale: punta ancora al valore (ora modificato) [1,2,3,4].

Per passare efficacemente un valore composto (come un array) per valore-copia, è necessario crearne manualmente una copia, in modo che il riferimento passato non punti ancora all'originale. Per esempio:

foo( a.slice() );

Valore composto (oggetto, matrice, ecc.) Che può essere passato tramite copia di riferimento

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

var obj = {
    a: 2
};

foo( obj );

obj.a; // 42

Qui, objfunge da wrapper per la proprietà primitiva scalare a. Quando viene passato a foo(..), viene passata una copia del objriferimento e impostata sul wrapperparametro. Ora possiamo usare il wrapperriferimento per accedere all'oggetto condiviso e aggiornare la sua proprietà. Al termine della funzione, obj.avedrà il valore aggiornato 42.

fonte

10 kianasirzadeh Nov 14 2019 at 07:48

beh, si tratta di "prestazioni" e "velocità" e nella semplice parola "gestione della memoria" in un linguaggio di programmazione.

in javascript possiamo mettere i valori in due livelli: type1 - objectse type2 - tutti gli altri tipi di valore come string& boolean& etc

se immagini la memoria come quadratini sotto cui in ognuno di essi può essere salvato un solo valore di tipo2:

ogni valore di tipo2 (verde) è un singolo quadrato mentre un valore di tipo1 (blu) è un gruppo di essi :

il punto è che se vuoi indicare un valore di tipo2, l'indirizzo è semplice ma se vuoi fare la stessa cosa per il valore di tipo1 non è affatto facile! :

e in una storia più complicata:

quindi qui i riferimenti possono salvarci:

mentre la freccia verde qui è una variabile tipica, quella viola è una variabile oggetto, quindi poiché la freccia verde (variabile tipica) ha solo un compito (e questo indica un valore tipico) non abbiamo bisogno di separare il suo valore da quindi spostiamo la freccia verde con il valore di quello ovunque vada e in tutti gli incarichi, funzioni e così via ...

ma non possiamo fare la stessa cosa con la freccia viola, potremmo voler spostare la cella "john" qui o molte altre cose ..., quindi la freccia viola rimarrà al suo posto e solo le frecce tipiche che le sono state assegnate si muoveranno ...

una situazione molto confusa è dove non puoi realizzare come cambia la tua variabile referenziata, diamo un'occhiata a un ottimo esempio:

let arr = [1, 2, 3, 4, 5]; //arr is an object now and a purple arrow is indicating it
let obj2 = arr; // now, obj2 is another purple arrow that is indicating the value of arr obj
let obj3 = ['a', 'b', 'c'];
obj2.push(6); // first pic below - making a new hand for the blue circle to point the 6
//obj2 = [1, 2, 3, 4, 5, 6]
//arr = [1, 2, 3, 4, 5, 6]
//we changed the blue circle object value (type1-value) and due to arr and obj2 are indicating that so both of them changed
obj2 = obj3; //next pic below - changing the direction of obj2 array from blue circle to orange circle so obj2 is no more [1,2,3,4,5,6] and it's no more about changing anything in it but we completely changed its direction and now obj2 is pointing to obj3
//obj2 = ['a', 'b', 'c'];
//obj3 = ['a', 'b', 'c'];

8 AshishSinghRawat Sep 26 2018 at 00:31

Questa è una spiegazione in più per passare per valore e passare per riferimento (JavaScript). In questo concetto, stanno parlando di passare la variabile per riferimento e passare la variabile per riferimento.

Passa per valore (tipo primitivo)

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
  • si applica a tutti i tipi primitivi in ​​JavaScript (stringa, numero, booleano, undefined e null).
  • a viene allocata una memoria (diciamo 0x001) eb crea una copia del valore in memoria (diciamo 0x002).
  • Quindi la modifica del valore di una variabile non influisce sull'altra, poiché entrambe risiedono in due posizioni diverse.

Passa per riferimento (oggetti)

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" }
  • Il motore JavaScript assegna l'oggetto alla variabile ce punta a un po 'di memoria, ad esempio (0x012).
  • Quando d = c, in questo passaggio dpunta alla stessa posizione (0x012).
  • La modifica del valore di qualsiasi modifica del valore per entrambe le variabili.
  • Le funzioni sono oggetti

Caso speciale, passaggio per riferimento (oggetti)

c = {"name" : "jane"};
console.log(c); // { "name" : "jane" }
console.log(d); // { "name" : "doe" }
  • L'operatore uguale (=) imposta un nuovo spazio di memoria o indirizzo
6 ZameerAnsari Aug 10 2017 at 19:43

condividendo ciò che so dei riferimenti in JavaScript

In JavaScript, quando si assegna un oggetto a una variabile, il valore assegnato alla variabile è un riferimento all'oggetto:

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 CPerkins Dec 24 2016 at 01:43

Semantica!! L'impostazione di definizioni concrete renderà necessariamente alcune risposte e commenti incompatibili poiché non descrivono la stessa cosa anche quando si usano le stesse parole e frasi, ma è fondamentale per superare la confusione (specialmente per i nuovi programmatori).

Prima di tutto, ci sono più livelli di astrazione che non tutti sembrano cogliere. I nuovi programmatori che hanno imparato sui linguaggi di quarta o quinta generazione possono avere difficoltà a comprendere concetti familiari agli assembly o programmatori C non suddivisi in fasi da puntatori a puntatori. Pass-by-reference non significa semplicemente la possibilità di modificare un oggetto referenziato utilizzando una variabile del parametro di funzione.

Variabile : concetto combinato di un simbolo che fa riferimento a un valore in una particolare posizione nella memoria. Questo termine è solitamente troppo carico per essere usato da solo nella discussione dei dettagli.

Simbolo : stringa di testo utilizzata per fare riferimento alla variabile (cioè il nome della variabile).

Valore : bit particolari archiviati in memoria e referenziati utilizzando il simbolo della variabile.

Posizione di memoria : dove è memorizzato il valore di una variabile. (La posizione stessa è rappresentata da un numero separato dal valore memorizzato nella posizione.)

Parametro di funzione : variabile dichiarata in una definizione di funzione, utilizzata per fare riferimento alle variabili passate alla funzione.

Argomento della funzione : variabile al di fuori della funzione che viene passata alla funzione dal chiamante.

Variabile oggetto : variabile il cui valore sottostante di base non è l '"oggetto" stesso, piuttosto il suo valore è un puntatore (valore della posizione di memoria) a un'altra posizione nella memoria in cui sono archiviati i dati effettivi dell'oggetto. Nella maggior parte dei linguaggi di generazione superiore, l'aspetto del "puntatore" è effettivamente nascosto dalla de-referenziazione automatica in vari contesti.

Variabile primitiva : variabile il cui valore È il valore effettivo. Anche questo concetto può essere complicato dall'auto-boxing e dai contesti simili a oggetti di vari linguaggi, ma l'idea generale è che il valore della variabile È il valore effettivo rappresentato dal simbolo della variabile piuttosto che un puntatore a un'altra posizione di memoria.

Gli argomenti ei parametri delle funzioni non sono la stessa cosa. Inoltre, il valore di una variabile non è l'oggetto della variabile (come già sottolineato da varie persone, ma apparentemente ignorato). Queste distinzioni sono fondamentali per una corretta comprensione.

Pass-by-value o Call-by-sharing (per oggetti) : il valore dell'argomento della funzione viene COPIATO in un'altra posizione di memoria a cui fa riferimento il simbolo del parametro della funzione (indipendentemente dal fatto che si trovi nello stack o nell'heap). In altre parole, il parametro della funzione ha ricevuto una copia del valore dell'argomento passato ... E (critico) il valore dell'argomento NON È MAI AGGIORNATO / ALTERATO / MODIFICATO dalla funzione chiamante. Ricorda, il valore di una variabile oggetto NON è l'oggetto stesso, piuttosto è il puntatore all'oggetto, quindi il passaggio di una variabile oggetto per valore copia il puntatore alla variabile del parametro della funzione. Il valore del parametro della funzione punta allo stesso identico oggetto in memoria. I dati dell'oggetto stesso possono essere modificati direttamente tramite il parametro della funzione, MA il valore dell'argomento della funzione NON È MAI AGGIORNATO, quindi continuerà a puntare allo stesso oggetto per tutto e anche dopo la chiamata della funzione (anche se i dati del suo oggetto sono stati alterati o se il al parametro della funzione viene assegnato un oggetto completamente diverso). Non è corretto concludere che l'argomento della funzione è stato passato per riferimento solo perché l'oggetto a cui si fa riferimento è aggiornabile tramite la variabile del parametro della funzione.

Call / Pass-by-reference : Il valore dell'argomento della funzione può / sarà aggiornato direttamente dal parametro della funzione corrispondente. Se aiuta, il parametro della funzione diventa un "alias" efficace per l'argomento: si riferiscono effettivamente allo stesso valore nella stessa posizione di memoria. Se l'argomento di una funzione è una variabile oggetto, la capacità di modificare i dati dell'oggetto non è diversa dal caso del valore di passaggio poiché il parametro della funzione punterà ancora allo stesso oggetto dell'argomento. Ma nel caso della variabile oggetto, se il parametro della funzione è impostato su un oggetto completamente diverso, anche l'argomento punterà allo stesso modo all'oggetto diverso - questo non accade nel caso del valore di passaggio.

JavaScript non passa per riferimento. Se leggi attentamente, ti renderai conto che tutte le opinioni contrarie fraintendono cosa si intende per valore di passaggio e concludono erroneamente che la capacità di aggiornare i dati di un oggetto tramite il parametro funzione è sinonimo di "passaggio per valore".

Clona / copia oggetto: viene creato un nuovo oggetto e vengono copiati i dati dell'oggetto originale. Può essere una copia profonda o una copia superficiale, ma il punto è che viene creato un nuovo oggetto. La creazione di una copia di un oggetto è un concetto separato dal valore di trasferimento. Alcuni linguaggi distinguono tra oggetti di classe e strutture (o simili) e possono avere un comportamento diverso per il passaggio di variabili di diversi tipi. Ma JavaScript non fa nulla di simile automaticamente quando passa le variabili oggetto. Ma l'assenza di clonazione automatica degli oggetti non si traduce in pass-by-reference.

4 georgeawg Jul 25 2018 at 22:47

JavaScript passa i tipi primitivi per valore e i tipi di oggetto per riferimento

Ora, alla gente piace litigare all'infinito sul fatto che "passa per riferimento" sia il modo corretto per descrivere ciò che Java et al. davvero. Il punto è questo:

  1. Il passaggio di un oggetto non copia l'oggetto.
  2. Un oggetto passato a una funzione può avere i suoi membri modificati dalla funzione.
  3. Un valore primitivo passato a una funzione non può essere modificato dalla funzione. Viene eseguita una copia.

Nel mio libro si chiama passare per riferimento.

- Brian Bi - Quali linguaggi di programmazione vengono passati per riferimento?


Aggiornare

Ecco una confutazione a questo:

Non è disponibile alcun "passaggio per riferimento" in JavaScript.

3 dpp Sep 16 2014 at 16:19

Il mio modo semplice per capire questo ...

  • Quando si chiama una funzione, si passa il contenuto (riferimento o valore) delle variabili argomento, non le variabili stesse.

    var var1 = 13;
    var var2 = { prop: 2 };
    
    //13 and var2's content (reference) are being passed here
    foo(var1, var2); 
    
  • All'interno della funzione, le variabili dei parametri inVar1e inVar2ricevono il contenuto passato.

    function foo(inVar1, inVar2){
        //changing contents of inVar1 and inVar2 won't affect variables outside
        inVar1 = 20;
        inVar2 = { prop: 7 };
    }
    
  • Dopo aver inVar2ricevuto il riferimento di { prop: 2 }, è possibile modificare il valore della proprietà dell'oggetto.

    function foo(inVar1, inVar2){
        inVar2.prop = 7; 
    }
    
3 JohnSonderson Nov 01 2014 at 19:43

Il passaggio di argomenti a una funzione in JavaScript è analogo al passaggio di parametri tramite il valore del puntatore in 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 DannyNiu Jul 28 2017 at 20:17

Per gli avvocati del linguaggio di programmazione, ho esaminato le seguenti sezioni di ECMAScript 5.1 (che è più facile da leggere rispetto all'ultima edizione) e sono arrivato a chiederlo sulla mailing list ECMAScript.

TL; DR : tutto viene passato per valore, ma le proprietà degli oggetti sono riferimenti e la definizione di oggetto è inquietantemente carente nello standard.

Costruzione di elenchi di argomenti

La sezione 11.2.4 "Elenchi di argomenti" dice quanto segue sulla produzione di una lista di argomenti composta da un solo argomento:

La produzione ArgumentList: AssignmentExpression viene valutata come segue:

  1. Sia ref il risultato della valutazione di AssignmentExpression.
  2. Sia arg GetValue (ref).
  3. Restituisce una lista il cui unico elemento è arg.

La sezione enumera anche i casi in cui l'elenco di argomenti ha 0 o> 1 argomenti.

Quindi, tutto viene passato per riferimento.

Accesso alle proprietà degli oggetti

Sezione 11.2.1 "Accessori per proprietà"

La produzione MemberExpression: MemberExpression [Expression] viene valutata come segue:

  1. Lascia che baseReference sia il risultato della valutazione di MemberExpression.
  2. Sia baseValue GetValue (baseReference).
  3. Lascia che propertyNameReference sia il risultato della valutazione di Expression.
  4. Consenti a propertyNameValue di essere GetValue (propertyNameReference).
  5. Chiama CheckObjectCoercible (baseValue).
  6. Lascia che propertyNameString sia ToString (propertyNameValue).
  7. Se la produzione sintattica che viene valutata è contenuta in un codice in modalità rigorosa, lasciate che strict sia vero, altrimenti lasciate che strict sia falso.
  8. Restituisce un valore di tipo Reference il cui valore di base è baseValue e il cui nome di riferimento è propertyNameString e il cui flag di modalità rigorosa è rigoroso.

Pertanto, le proprietà degli oggetti sono sempre disponibili come riferimento.

A riferimento

È descritto nella sezione 8.7 "Il tipo di specifica di riferimento", che i riferimenti non sono tipi reali nel linguaggio - sono usati solo per descrivere il comportamento degli operatori di cancellazione, tipo di e di assegnazione.

Definizione di "oggetto"

È definito nell'edizione 5.1 che "Un oggetto è una raccolta di proprietà". Pertanto, possiamo dedurre che il valore dell'oggetto è la collezione, ma quanto a quale sia il valore della collezione è mal definito nelle specifiche e richiede un po 'di sforzo per capire.

3 miguelr Apr 20 2019 at 13:06

I documenti MDN lo spiegano chiaramente, senza essere troppo prolissi:

I parametri di una chiamata di funzione sono gli argomenti della funzione . Gli argomenti vengono passati alle funzioni in base al valore . Se la funzione cambia il valore di un argomento, questa modifica non si riflette globalmente o nella funzione chiamante. Tuttavia, anche i riferimenti agli oggetti sono valori e sono speciali: se la funzione cambia le proprietà dell'oggetto riferito, quel cambiamento è visibile al di fuori della funzione, (...)

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

2 JonasWilms Jul 26 2020 at 22:32

osservazione: se un osservatore non ha modo di esaminare la memoria sottostante del motore, non c'è modo di determinare se un valore immutabile viene copiato o un riferimento viene passato.

JavaScript è più o meno agnostico rispetto al modello di memoria sottostante. Non esiste un riferimento ². JavaScript ha valori . Due variabili possono contenere lo stesso valore (o più preciso: due record di ambiente possono associare lo stesso valore). L'unico tipo di valori che possono essere modificati sono gli oggetti tramite le loro operazioni astratte [[Get]] e [[Set]]. Se ti dimentichi di computer e memoria, questo è tutto ciò di cui hai bisogno per descrivere il comportamento di JavaScripts e ti consente di comprendere le specifiche.

 let a = { prop: 1 };
 let b = a; // a and b hold the same value
 a.prop = "test"; // the object gets mutated, can be observed through both a and b
 b = { prop: 2 }; // b holds now a different value
 

Ora potresti chiederti come due variabili possono mantenere lo stesso valore su un computer. Potresti quindi esaminare il codice sorgente di un motore JavaScript e molto probabilmente troverai qualcosa che un programmatore del linguaggio in cui è stato scritto il motore chiamerebbe un riferimento.

Quindi in effetti puoi dire che JavaScript è "passa per valore", mentre il valore può essere condiviso, puoi dire che JavaScript è "passa per riferimento", che potrebbe essere un'utile astrazione logica per i programmatori di linguaggi di basso livello, o per te potrebbe chiamare il comportamento "chiama condividendo". Poiché non esiste un riferimento in JavaScript, tutti questi non sono né sbagliati né puntuali. Pertanto non credo che la risposta sia particolarmente utile per la ricerca.

² Il termine Riferimento nella specifica non è un riferimento nel senso tradizionale. È un contenitore per un oggetto e il nome di una proprietà, ed è un valore intermedio (ad es. a.bRestituisce Reference { value = a, name = "b" }). Il termine riferimento a volte compare anche nella specifica in sezioni non correlate.

1 lid May 17 2014 at 08:01

La spiegazione più succinta che ho trovato era nella guida allo stile di AirBNB :

  • Primitive : quando accedi a un tipo primitivo, lavori direttamente sul suo valore

    • corda
    • numero
    • booleano
    • nullo
    • non definito

Per esempio:

var foo = 1,
    bar = foo;

bar = 9;

console.log(foo, bar); // => 1, 9
  • Complesso : quando accedi a un tipo complesso, lavori su un riferimento al suo valore

    • oggetto
    • Vettore
    • funzione

Per esempio:

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

bar[0] = 9;

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

In altre parole, i tipi primitivi vengono effettivamente passati per valore, mentre i tipi complessi vengono passati per riferimento.

1 steviejay Oct 29 2016 at 21:50

Ho letto queste risposte più volte, ma non le ho VERAMENTE ricevute fino a quando non ho appreso la definizione tecnica di "Chiama condividendo" come definita da Barbara Liskov

La semantica della chiamata per condivisione differisce dalla chiamata per riferimento in quanto le assegnazioni agli argomenti della funzione all'interno della funzione non sono visibili al chiamante (a differenza della semantica di riferimento) [citazione necessaria], quindi ad es. Se una variabile è stata passata, nonèpossibile per simulare un'assegnazione su quella variabile nell'ambito del chiamante. Tuttavia, poiché la funzione ha accesso allo stesso oggetto del chiamante (non viene eseguita alcuna copia), le mutazioni a quegli oggetti, se gli oggetti sono mutabili, all'interno della funzione sono visibili al chiamante, che può sembrare differire dalla chiamata per valore semantica. Le mutazioni di un oggetto modificabile all'interno della funzione sono visibili al chiamante perché l'oggetto non viene copiato o clonato, ma condiviso.

Cioè, i riferimenti ai parametri sono modificabili se si accede al valore del parametro stesso. D'altra parte, l'assegnazione a un parametro scomparirà dopo la valutazione e non sarà accessibile al chiamante della funzione.

1 Narayon Oct 19 2016 at 19:07

In un linguaggio di basso livello, se vuoi passare una variabile per riferimento, devi usare una sintassi specifica nella creazione della funzione:

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

Il &ageè un riferimento a myAge, ma se si desidera che il valore è necessario convertire il riferimento, utilizzando *age.

JavaScript è un linguaggio di alto livello che esegue questa conversione per te.

Quindi, sebbene gli oggetti vengano passati per riferimento, il linguaggio converte il parametro di riferimento nel valore. Non è necessario utilizzare &, sulla definizione della funzione, per passarla per riferimento, né *, sul corpo della funzione, per convertire il riferimento al valore, JavaScript lo fa per te.

Ecco perché quando provi a cambiare un oggetto all'interno di una funzione, sostituendone il valore (cioè age = {value:5}), la modifica non persiste, ma se cambi le sue proprietà (cioè age.value = 5), lo fa.

Per saperne di più