2012-02-24 8 views
50

So che in JS, gli oggetti vengono passati per riferimento, ad esempio:Perché questo oggetto non viene passato per riferimento quando si assegna qualcos'altro?

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

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

Ma perché non funziona sotto:

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

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

ho impostato l'oggetto {} (vuoto) ma dice ancora foo.

Qualcuno può spiegare la logica dietro questo?

+7

Questo perché gli oggetti non vengono passati per riferimento;) – delnan

+0

@delnan Ma ci sono. Guarda come 'currentObject' cambia' $ scope.countries' -> http://jsfiddle.net/ay1wpr5L/2/ – CodyBugstein

+0

@Imray Questo non viene passato per riferimento, come spiegato da più risposte di seguito. – delnan

risposta

66

Se hai familiarità con i puntatori, è un'analogia che puoi prendere. In realtà stai passando un puntatore, quindi obj.someProperty farebbe il dereferenziamento a quella proprietà e in realtà la sostituirà, mentre semplicemente ignorando obj ucciderebbe il puntatore e non sovrascriverebbe l'oggetto.

+2

analogia molto bella. – Dampsquid

+2

+1 - ben fatto e più chiaro della mia risposta (supponendo che OP conosca i puntatori) –

+0

@AdamRackis I ha fatto +1 sulla tua risposta perché utilizza la terminologia JS e ha portato in mente l'analogia;) –

29

Poiché JavaScript trasmette effettivamente gli oggetti tramite passaggio-, copia-riferimento.

Quando si passa my_obj nella vostra funzione test, una copia di un riferimento a tale oggetto è passato in. Di conseguenza, quando si ri-assegnare l'oggetto in test, sei davvero solo ri-assegnazione di un copia di un riferimento all'oggetto originale; il tuo originale my_obj rimane invariato.

+0

Se è una copia, allora perché funziona nel primo esempio che ho pubblicato? Nel mio primo esempio anche questo dovrebbe rimanere non toccato? – Dev555

+2

@ Dev555 - è una copia di un ** riferimento a ** l'oggetto - ho modificato per essere più chiaro. Nel tuo primo caso, obj è una copia di un riferimento che punta al tuo oggetto reale. L'aggiunta di una proprietà funzionerà correttamente. Se fossi lì di persona, potrei disegnare alcune immagini di scatole con frecce che penso sarebbero di grande aiuto :) –

+0

Quindi, come inizializzare gli oggetti? Inizialmente avevo questo 'instance.initGrid ($ (instance.mainEntityContainer), instance.mainEntityList); instance.initGrid ($ (instance.dependantEntityContainer), instance.dependantEntityList); ', ho dovuto trasformarlo in:' instance.mainEntityList = instance.initGrid ($ (instance.mainEntityContainer)); instance.dependantEntityList = instance.initGrid ($ (instance.dependantEntityContainer)); 'Ancora mi chiedo come" liberare memoria "dalle precedenti inizializzazioni. Se devo solo rendere instance.dependantEntityList = null da qualche parte? Ho "= nuovo ..." dentro. – Alexander

26

Perché si sta sovrascrivendo il riferimento, non l'oggetto.

// Create a new object and assign a reference to it 
// to the variable my_obj 
var my_obj = { name: 'foo' }; 

// Pass the reference to the test function 
test(my_obj); 

// Assign the reference to a variable called obj 
// (since that is the first argument) 
function test(obj) { 
// Create a new (empty) object and assign a reference to it to obj 
// This replaces the existing REFERENCE 
    obj = {}; 
} 
// my_obj still has a reference to the original object, 
// because my_obj wasn't overwritten 
alert(my_obj.name); // foo 
+0

Si sta sovrascrivendo la copia del riferimento, non il riferimento. Se dovessi sovrascrivere il riferimento stesso, tutto andrebbe bene. – Alex

+3

Quindi, come vorresti sovrascrivere 'my_obj' se lo volessi? – CodyBugstein

+0

@CodyBugstein Avvolgilo in un altro oggetto. Un'altra risposta a questa domanda, http://stackoverflow.com/a/13452001/841830, mostra questo, insieme ad altri possibili approcci. Oppure, se non ti piacciono questi approcci, penso che la risposta sia fondamentalmente: non puoi sovrascrivere 'my_obj'. –

5

Javascript manca il supporto per il passaggio per riferimento (anche se gli oggetti vengono passati per riferimento e si mantiene il riferimento fino a quando non viene sovrascritto con assegnazione ad es. Utilizzando =), ma si può imitare ref parola chiave di C# utilizzando la seguente tecnica:

function test(obj) { 
    obj.Value = {}; 
    //obj.Value = {name:"changed"}; 
} 

var my_obj = { name: 'foo' }; 

(function() 
{ 
    my_obj = {Value: my_obj}; 
    var $return = test(my_obj); 
    my_obj = my_obj.Value; 
    return $return; 
}).call(this); 

alert(my_obj.name); // undefined, as expected 
        // In the question this returns "foo" because 
        // assignment causes dereference 

Naturalmente è possibile utilizzare variabili globali e funzione di chiamata senza argomenti, in tal caso, i riferimenti non sono mancati in questo modo:

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

Se si dispone di tutto il codice in chiusura, poi le cose sono più semplici e, soprattutto, come globali non inquina namespace globale:

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

Le "variabili globali all'interno chiusura" di cui sopra -technique è bello se si deve port per Javascript qualche codice C# che ha argomenti ref. Per esempio. Il seguente codice C#:

void MainLoop() 
{ 
    // ... 
    MyStruct pt1 = CreateMyStruct(1); 
    MyStruct pt2 = CreateMyStruct(2); 
    SwapPoints(ref pt1, ref pt2); 
    // ... 
} 
void SwapPoints(ref MyStruct pt1, ref MyStruct pt2) 
{ 
    MyStruct tmp = pt1; 
    pt1 = pt2; 
    pt2 = tmp; 
} 

potrebbe essere portato a JavaScript utilizzando qualcosa di simile:

(function(){ 
    var pt1, pt2; 
    function CreateMyStruct(myvar) 
    { 
     return {"myvar":myvar} 
    } 
    function MainLoop() 
    { 
     // ... 
     pt1 = CreateMyStruct(1); 
     pt2 = CreateMyStruct(2); 
     console.log("ORIG:",pt1,pt2); 
     SwapPoints(); 
     console.log("SWAPPED:",pt1,pt2); 
     // ... 
    } 
    function SwapPoints() 
    { 
     var tmp = pt1; 
     pt1 = pt2; 
     pt2 = tmp; 
    } 
    MainLoop(); 

}).call(this); 

o se è indispensabile utilizzare le variabili e gli argomenti della funzione locale, allora la soluzione può essere basata sul primo esempio di la mia risposta in questo modo:

(function(){ 
    function CreateMyStruct(myvar) 
    { 
     return {"myvar":myvar} 
    } 
    function MainLoop() 
    { 
     // ... 
     var pt1 = CreateMyStruct(1); 
     var pt2 = CreateMyStruct(2); 
     console.log("ORIG:",pt1,pt2); 

     (function() 
     { 
     pt1 = {Value: pt1}; 
     pt2 = {Value: pt2}; 
     var $return = SwapPoints(pt1, pt2); 
     pt1 = pt1.Value; 
     pt2 = pt2.Value; 
     return $return; 
     }).call(this); 

     console.log("SWAPPED:",pt1,pt2); 
     // ... 
    } 
    function SwapPoints(pt1, pt2) 
    { 
     var tmp = pt1.Value; 
     pt1.Value = pt2.Value; 
     pt2.Value = tmp; 
    } 
    MainLoop(); 
}).call(this); 

hanno davvero dire che Javascript manca molto quando non ha nativo ref! Il codice sarebbe molto più semplice.

Problemi correlati