2011-07-07 15 views
290

Estoy buscando un buen material de lectura exhaustivo sobre cuándo JavaScript pasa algo por valor y cuándo por referencia y cuando modificar un artículo pasado afecta el valor fuera de una función y cuando no. También me interesa cuando la asignación a otra variable es por referencia vs. valor y si eso sigue reglas diferentes que pasar como un parámetro de función.Javascript por referencia vs. por valor

He realizado muchas búsquedas y he encontrado muchos ejemplos específicos (muchos de ellos aquí en SO) a partir de los cuales puedo comenzar a juntar partes de las reglas reales, pero todavía no he encontrado ninguno, documento bien escrito que lo describe todo.

Además, ¿hay formas en el lenguaje para controlar si algo se pasa por referencia o por valor?

Estos son algunos de los tipos de preguntas que quiero entender. Estos son solo ejemplos: en realidad estoy buscando entender las reglas que usa el lenguaje, no solo las respuestas a ejemplos específicos. Pero, he aquí algunos ejemplos:

function f(a,b,c) { 
    a = 3; 
    b.push("foo"); 
    c.first = false; 
} 

var x = 4; 
var y = ["eeny", "miny", "mo"]; 
var z = {first: true}; 
f(x,y,z); 

Cuando son los contenidos de x, y, z cambiado fuera del alcance de f para todos los diferentes tipos?

function f() { 
    var a = ["1", "2", "3"]; 
    var b = a[1]; 
    a[1] = "4"; 
    // what is the value of b now for all possible data types that the array in "a" might hold? 
} 

function f() { 
    var a = [{yellow: "blue"}, {red: "cyan"}, {green: "magenta"}]; 
    var b = a[1]; 
    a[1].red = "tan"; 
    // what is the value of b now and why? 
    b.red = "black"; 
    // did the value of a[1].red change when I assigned to b.red? 
} 

Si quiero hacer una copia completamente independiente de un objeto (no hay referencias de ningún tipo), ¿cuál es la mejor manera práctica de hacerlo?

+0

Esta es una lectura muy recomendable: [wiki: Estrategia de Evaluación] (http://en.wikipedia.org/wiki/Evaluation_strategy). Prefiero Call By Object Sharing para evitar tal confusión y dejar Call By Reference para realmente significar algo más, donde se aplique. Para hacer un "clon", considere 'jQuery.extend' (clon superficial solo!) O similar proporcionado por su elección de marco. (Creo que ECMA ed.5 introduce 'Object.clone' ...?). Se puede lograr un "clon profundo" con la serialización a JSON y viceversa, donde dicha operación se conserva (puede que no siempre sea así). Estoy seguro de que también hay otras funciones diseñadas para esto :) –

+0

Creo que jQuery.extend ahora (1.8.x +) admite la clonación profunda (utilizando un parámetro booleano opcional) –

+3

En los años transcurridos desde esta pregunta, he descubierto que es Es más fácil explicar este problema a los novatos de JavaScript (particularmente a las personas que conocen C/C++) al decir esto: "En Javascript, los objetos se pasan por puntero y todo lo demás se pasa por valor". – jfriend00

Respuesta

495

Mi entendimiento es que esto es en realidad muy simple:

  • Javascript es siempre paso por valor, pero cuando una variable se refiere a un objeto (incluyendo arrays), el "valor" es una referencia a el objeto.
  • Al cambiar el valor de una variable nunca cambia la primitiva u objeto subyacente, simplemente apunta la variable a una nueva primitiva u objeto.
  • Sin embargo, cambiar una propiedad de un objeto al que hace referencia una variable no cambia el objeto subyacente.

lo tanto, para trabajar a través de algunos de sus ejemplos:

function f(a,b,c) { 
    // Argument a is re-assigned to a new value. 
    // The object or primitive referenced by the original a is unchanged. 
    a = 3; 
    // Calling b.push changes its properties - it adds 
    // a new property b[b.length] with the value "foo". 
    // So the object referenced by b has been changed. 
    b.push("foo"); 
    // The "first" property of argument c has been changed. 
    // So the object referenced by c has been changed (unless c is a primitive) 
    c.first = false; 
} 

var x = 4; 
var y = ["eeny", "miny", "mo"]; 
var z = {first: true}; 
f(x,y,z); 
console.log(x, y, z.first); // 4, ["eeny", "miny", "mo", "foo"], false 

Ejemplo 2:

var a = ["1", "2", {foo:"bar"}]; 
var b = a[1]; // b is now "2"; 
var c = a[2]; // c now references {foo:"bar"} 
a[1] = "4"; // a is now ["1", "4", {foo:"bar"}]; b still has the value 
       // it had at the time of assignment 
a[2] = "5"; // a is now ["1", "4", "5"]; c still has the value 
       // it had at the time of assignment, i.e. a reference to 
       // the object {foo:"bar"} 
console.log(b, c.foo); // "2" "bar" 
+51

Si bien es técnicamente cierto, prefiero decir que JavaScript es [Pass By Object Sharing] (http://en.wikipedia.org/wiki/Evaluation_strategy#Call_by_sharing). Evita esa confusión y se mueve a una vista de "alto nivel". –

+1

@pst - Eso tiene sentido, pero tenga en cuenta los problemas planteados en la página de Wikipedia referenciada: la mayoría de las personas no usa el término, y la comunidad (grande) de Java llama a esto "paso por valor". Aún así, estoy de acuerdo en que es un poco confuso. – nrabinowitz

+6

¿A qué "confusión" se está refiriendo? Para mí, "pass-by-value" está perfectamente claro. – MEMark

22

Sí, Javascript siempre pasa por valor, pero en una matriz u objeto, el valor es una referencia a él, por lo que puede 'cambiar' los contenidos.

embargo, creo que ya ha leído en SO; here que haya la documentación que desee:

http://snook.ca/archives/javascript/javascript_pass

+1

Si bien es técnicamente cierto, prefiero decir que JavaScript es [Pass By Object Sharing] (http://en.wikipedia.org/wiki/Evaluation_strategy#Call_by_sharing). Evita esa confusión y se mueve a una vista de "alto nivel". –

+0

Hice un violín para jugar con esto un poco: http://jsfiddle.net/tkane2000/7weKS/1/ – tkane2000

42

Javascript siempre pasa por valor. Sin embargo, si pasa un objeto a una función, el "valor" es realmente una referencia a ese objeto, por lo que la función puede modificar las propiedades de ese objeto pero no hacer que la variable fuera de la función apunte a otro objeto.

Un ejemplo:

function changeParam(x, y, z) { 
    x = 3; 
    y = "new string"; 
    z["key2"] = "new"; 
    z["key3"] = "newer"; 

    z = {"new" : "object"}; 
} 

var a = 1, 
    b = "something", 
    c = {"key1" : "whatever", "key2" : "original value"}; 

changeParam(a, b, c); 

// at this point a is still 1 
// b is still "something" 
// c still points to the same object but its properties have been updated 
// so it is now {"key1" : "whatever", "key2" : "new", "key3" : "newer"} 
// c definitely doesn't point to the new object created as the last line 
// of the function with z = ... 
+4

'Array' es un objeto, así que también cambiará. –

9
  1. variable de tipo primitivo como cadena, número son siempre pasar como pase por valor.
  2. La matriz y el objeto se pasan como pase por referencia o pasan por valor basado en estas dos condiciones.

    • si cambia el valor de ese Objeto o matriz con Objeto o Matriz nuevo, entonces pasa por Valor.

      object1 = {item: "car"}; array1=[1,2,3];

    aquí está asignando nuevo objeto o una matriz de edad one.you no están cambiando el valor de la propiedad de edad object.so es pasar por valor.

    • si va a cambiar un valor de propiedad de un objeto o una matriz, entonces es el paso por referencia.

      object1.item= "car"; array1[0]=9;

    aquí se están cambiando un valor de propiedad de edad object.you no están asignando nuevo objeto o una matriz de edad one.so es pase por referencia.

Código

function passVar(object1, object2, number1) { 

     object1.key1= "laptop"; 
     object2 = { 
      key2: "computer" 
     }; 
     number1 = number1 + 1; 
    } 

    var object1 = { 
     key1: "car" 
    }; 
    var object2 = { 
     key2: "bike" 
    }; 
    var number1 = 10; 

    passVar(object1, object2, number1); 
    console.log(object1.key1); 
    console.log(object2.key2); 
    console.log(number1); 

Output: - 
    laptop 
    bike 
    10 
+0

No. Todo pasa por valor, siempre. – newacct

+1

simplemente ponga el código anterior en su consola y vea ... el valor obtuvo un cambio. –

+10

Hay un debate terminológico honrado por el tiempo sobre llamar a lo que Javascript "pasa por referencia". Prefiero dar un paso al costado de ese debate y llamar lo que hace JS para objetos y matrices "pasar por puntero". Las matrices y los objetos siempre pasan por un puntero. Si modifica lo que se pasó (accediendo al puntero), el original se modifica. Si asigna una matriz u objeto diferente a la variable de puntero, entonces el original no se modifica porque su variable ahora "apunta" a una matriz u objeto diferente. Gran parte de esto es un "debate de terminología", porque no hay debate sobre lo que realmente sucede. – jfriend00

Cuestiones relacionadas