2012-02-24 14 views
50

sé que en JS, los objetos se pasan por referencia, por ejemplo:¿Por qué este objeto no se pasa por referencia al asignarle algo más?

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

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

Pero ¿por qué no el siguiente trabajo:

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

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

que han establecido el objeto a {} (vacío) pero todavía dice foo.

¿Alguien puede explicar la lógica detrás de esto?

+7

Eso es porque los objetos no se pasan por referencia;) – delnan

+0

@delnan Pero los hay. Vea cómo 'currentObject' cambia' $ scope.countries' -> http://jsfiddle.net/ay1wpr5L/2/ – CodyBugstein

+0

@Imray Eso no pasa por referencia, como lo explican las respuestas múltiples a continuación. – delnan

Respuesta

66

Si está familiarizado con los punteros, esa es una analogía que puede tomar. De hecho, está pasando un puntero, por lo que obj.someProperty haría referencia a esa propiedad y, de hecho, anulará eso, mientras que simplemente anular obj matará al puntero y no sobrescribirá el objeto.

+2

muy buena analogía. – Dampsquid

+2

+1 - muy bien hecho, y más claro que mi respuesta (suponiendo que OP sepa sobre punteros) –

+0

@ AdamRackis He respondido I + 1 porque usa la terminología JS y trajo la analogía anterior a la mente;) –

29

Porque JavaScript pasa objetos por pass-by-copia -reference.

Cuando se pasa my_obj en su función test, una copia de una referencia a ese objeto se pasa en. Como resultado, cuando se vuelva a asignar el objeto en test, estás realmente sólo re-asignación de una copia de una referencia al objeto original; su my_obj original permanece sin cambios.

+0

Si es copia, entonces ¿por qué funciona en el primer ejemplo que he publicado? ¿En mi primer ejemplo también debería permanecer sin tocar? – Dev555

+2

@ Dev555 - es una copia de ** una referencia a ** el objeto - he editado para ser más claro. En su primer caso, obj es una copia de una referencia que apunta a su objeto real. Agregar una propiedad funcionará bien. Si estuviera allí en persona, podría dibujar algunas imágenes de cajas con flechas que creo que ayudarían mucho :) –

+0

Entonces, ¿cómo inicializar objetos? Inicialmente tenía este 'instance.initGrid ($ (instance.mainEntityContainer), instance.mainEntityList); instance.initGrid ($ (instance.dependantEntityContainer), instance.dependantEntityList); ', tuve que transformar esto en lo siguiente:' instance.mainEntityList = instance.initGrid ($ (instance.mainEntityContainer)); instance.dependantEntityList = instance.initGrid ($ (instance.dependantEntityContainer)); 'Todavía me pregunto cómo" liberar memoria "de inicializaciones anteriores. Si solo tengo que hacer instance.dependantEntityList = null en alguna parte? Tengo "= nuevo ..." adentro. – Alexander

26

Porque está sobreescribiendo la referencia, no el objeto.

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

Está sobrescribiendo la copia de la referencia, no la referencia. Si sobrescribiera la referencia en sí, todo estaría bien. – Alex

+3

Entonces, ¿cómo sobrescribirías 'my_obj' si quisieras? – CodyBugstein

+0

@CodyBugstein Envuélvalo en otro objeto. Otra respuesta sobre esta pregunta, http://stackoverflow.com/a/13452001/841830, muestra esto, junto con algunos otros enfoques posibles. O bien, si no le gusta ninguno de estos enfoques, creo que la respuesta es básicamente: no puede sobrescribir 'my_obj'. –

5

Javascript carece de soporte para pasar por referencia (aunque los objetos se pasan por referencia y la referencia se mantiene siempre que no se sobreescrito con asignación por ejemplo. Usando =), pero se puede imitar ref palabra clave de C# utilizando la siguiente técnica:

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 

por supuesto, puede utilizar variables globales y la función de llamada sin argumentos, en cuyo caso las referencias no se pierden de esta manera:

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

Si usted tiene todo el código en el cierre, entonces las cosas son más simples y por encima como variables globales no contamina espacio de nombres global:

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

Las variables globales "en el interior de cierre" por encima -technique es agradable si tiene al puerto Javascript algún código de C# que tiene ref argumentos. P.ej. El siguiente código 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; 
} 

podría ser portado a Javascript usando algo como:

(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 si es imprescindible el uso de variables y parámetros de una función local, entonces la solución se puede basar en el primer ejemplo de mi respuesta como esta:

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

realmente tiene que decir que carece de Javascript mucho cuando se tiene no es nativo ref! El código sería mucho más simple.

Cuestiones relacionadas