¿Es JavaScript un lenguaje de paso por referencia o de paso por valor?


1405

Los tipos primitivos (número, cadena, etc.) se pasan por valor, pero los objetos son desconocidos, ya que ambos pueden pasar por valor (en caso de que consideremos que una variable que contiene un objeto es, de hecho, una referencia al objeto ) y pasado por referencia (cuando consideramos que la variable del objeto contiene el objeto mismo).

Aunque realmente no importa al final, quiero saber cuál es la forma correcta de presentar los argumentos pasando convenciones. ¿Hay un extracto de la especificación de JavaScript, que define cuál debería ser la semántica al respecto?


2
Creo que accidentalmente volteó sus definiciones de pasado por valor y pasado por referencia ... "pasado por valor (en caso de que consideremos que una variable que contiene un objeto es de hecho una referencia al objeto) y pasó -por-referencia (cuando consideramos que la variable del objeto contiene el objeto mismo) "
Niko Bellic

55
Si. Independientemente de la sintaxis, en cualquier llamada de función en cualquier lenguaje de programación, la referencia de paso significa que los datos asociados con la variable pasada no se copian cuando se pasan a la función y, por lo tanto, cualquier modificación realizada por la función a la variable pasada se conservará en el programa después de que finalice la llamada a la función. El paso por valor significa que los datos asociados con la variable se copian realmente cuando se pasan a la función y cualquier modificación realizada por dicha función a dicha variable se perderá cuando la variable salga del alcance del cuerpo de la función cuando la función regrese.
John Sonderson

55
Esta vieja pregunta es algo tóxica porque su respuesta muy votada es incorrecta. JavaScript es estrictamente paso por valor .
Puntiagudo

66
@DanailNachev La terminología es lamentablemente confusa. La cuestión es que "pasar por valor" y "pasar por referencia" son términos que son anteriores a muchas características más modernas del lenguaje de programación. Las palabras "valor" y "referencia" se refieren específicamente al parámetro tal como aparece en la expresión de llamada de función. JavaScript siempre evalúa cada expresión en una lista de parámetros de llamada de función antes de llamar a la función, por lo que los parámetros son siempre valores. La parte confusa es que las referencias a objetos son valores comunes de JavaScript. Sin embargo, eso no lo convierte en un lenguaje de "pasar por referencia".
Puntiagudo

2
@DanailNachev "pasar por referencia" significa específicamente que si tiene la var x=3, y=x; f(x); alert(y === x);función f()puede hacer el informe de alerta falsey no true. En JavaScript, eso no es posible, por lo que no es una referencia de paso. Es bueno que sea posible pasar referencias a objetos modificables, pero eso no es lo que significa "pasar por referencia". Como dije, es una pena que la terminología sea tan confusa.
Puntiagudo

Respuestas:


1599

Es interesante en JavaScript. Considere este ejemplo:

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

Esto produce la salida:

10
changed
unchanged
  • Si obj1no fuera una referencia en absoluto, el cambio obj1.itemno tendría ningún efecto en el obj1exterior de la función.
  • Si el argumento fuera una referencia adecuada, entonces todo habría cambiado. numsería 100, y obj2.itemleería "changed".

En cambio, la situación es que el elemento pasado se pasa por valor. Pero el elemento que se pasa por valor es en sí mismo una referencia. Técnicamente, esto se llama llamada por compartir .

En términos prácticos, esto significa que si cambia el parámetro en sí (como con numy obj2), eso no afectará el elemento que se introdujo en el parámetro. Pero si cambia los INTERNOS del parámetro, se propagará una copia de seguridad (como con obj1).


32
Esto es exactamente igual (o al menos semánticamente) que C #. El objeto tiene dos tipos: Valor (tipos primitivos) y Referencia.
Peter Lee

53
Creo que esto también se usa en Java: referencia por valor.
Jpnh

296
La verdadera razón es que dentro de changeStuff, num, obj1 y obj2 son referencias. Cuando cambia la itempropiedad del objeto al que hace referencia obj1, está cambiando el valor de la propiedad del elemento que se configuró originalmente como "sin cambios". Cuando asigna a obj2 un valor de {item: "changed"}, está cambiando la referencia a un nuevo objeto (que inmediatamente queda fuera de alcance cuando la función sale). Se hace más evidente lo que sucede si nombra los parámetros de la función cosas como numf, obj1f y obj2f. Entonces ves que los params estaban ocultando los nombres var externos.
jinglesthula

13
@BartoNaz No realmente. Lo que desea es pasar la referencia por referencia, en lugar de pasar la referencia por valor. Pero JavaScript siempre pasa la referencia por valor, al igual que pasa todo lo demás por valor. (A modo de comparación, C # tiene un comportamiento de referencia de paso por valor similar a JavaScript y Java, pero le permite especificar la referencia de paso por referencia con la refpalabra clave). Por lo general, la función devolvería el nuevo objeto y la asignación en el punto donde llama a la función. Por ejemplo, en foo = GetNewFoo();lugar deGetNewFoo(foo);
Tim Goodman

56
Aunque esta respuesta es la más popular, puede ser un poco confusa porque dice "Si fuera puramente pasar por valor". JavaScript es puro paso por valor. Pero el valor que se pasa es una referencia. Esto no está limitado a pasar parámetros en absoluto. Simplemente podría copiar la variable var obj1 = { item: 'unchanged' }; var obj2 = obj1; obj2.item = 'changed';y observaría el mismo efecto que en su ejemplo. Por lo tanto, personalmente remito la respuesta de Tim Goodman
chiccodoro

476

Siempre pasa por valor, pero para los objetos el valor de la variable es una referencia. Debido a esto, cuando pasa un objeto y cambia sus miembros , esos cambios persisten fuera de la función. Esto hace que parezca pasar por referencia. Pero si realmente cambia el valor de la variable del objeto, verá que el cambio no persiste, lo que demuestra que realmente pasa por valor.

Ejemplo:

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

Salida:

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

before changeMember: foo
in changeMember: bar
after changeMember: bar

14
@ daylight: en realidad, te equivocas; si fue pasado por const ref tratando de hacer changeObject causaría un error, en lugar de simplemente fallar. Intente asignar un nuevo valor a una referencia constante en C ++ y el compilador lo rechaza. En términos de usuario, esa es la diferencia entre pasar por valor y pasar por referencia constante.
deworde

55
@ daylight: no es constante ref. En changeObject, he cambiado xpara contener una referencia al nuevo objeto. x = {member:"bar"};es equivalente a x = new Object(); x.member = "bar"; lo que estoy diciendo también es cierto de C #, por cierto.
Tim Goodman

2
@daylight: Para C #, se puede ver esto desde fuera de la función, si se utiliza la refpalabra clave que puede pasar la referencia por referencia (en lugar del predeterminado de pasar la referencia de valor), y entonces el cambio a punto a una new Object() le persistir .
Tim Goodman

11
@adityamenon Es difícil responder "por qué", pero quisiera señalar que los diseñadores de Java y C # hicieron una elección similar; Esto no es solo una rareza de JavaScript. Realmente, es un paso por valor muy consistente, lo que lo hace confuso para las personas es que un valor puede ser una referencia. No es muy diferente a pasar un puntero (por valor) en C ++ y luego desreferenciarlo para establecer los miembros. Nadie se sorprendería de que ese cambio persista. Pero debido a que estos lenguajes abstraen el puntero y silenciosamente hacen la referencia por usted, la gente se confunde.
Tim Goodman

41
En otras palabras, lo confuso aquí no es pasar por valor / pasar por referencia. Todo es paso por valor, punto final. Lo confuso es que no puede pasar un objeto, ni puede almacenar un objeto en una variable. Cada vez que crees que estás haciendo eso, en realidad estás pasando o almacenando una referencia a ese objeto. Pero cuando va a acceder a sus miembros, ocurre una desreferencia silenciosa que perpetúa la ficción de que su variable contenía el objeto real.
Tim Goodman

150

La variable no "retiene" el objeto; Tiene una referencia. Puede asignar esa referencia a otra variable, y ahora ambas hacen referencia al mismo objeto. Siempre pasa por valor (incluso cuando ese valor es una referencia ...).

No hay forma de alterar el valor que posee una variable pasada como parámetro, lo que sería posible si JavaScript admitiera pasar por referencia.


2
Esto me confunde un poco. ¿No está pasando una referencia pasa por referencia?

8
El autor significa que al pasar una referencia, está pasando un valor de referencia (otra forma de pensar es pasar el valor de la dirección de memoria). Por eso, si redeclara el objeto, el original no cambia, porque está creando un nuevo objeto en una ubicación de memoria diferente. Si cambia una propiedad, el objeto original cambia porque lo cambió en la ubicación de memoria original (que no fue reasignada).
Huy-Anh Hoang

113

Mis dos centavos ... Así es como lo entiendo. (Siéntase libre de corregirme si me equivoco)

Es hora de tirar todo lo que sabe sobre pasar por valor / referencia.

Porque en JavaScript, no importa si se pasa por valor o por referencia o lo que sea. Lo que importa es la mutación frente a la asignación de los parámetros pasados ​​a una función.

OK, déjame hacer todo lo posible para explicar lo que quiero decir. Digamos que tienes algunos objetos.

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

Lo que hemos hecho es "asignación" ... Hemos asignado 2 objetos vacíos separados a las variables "objeto1" y "objeto2".

Ahora, digamos que nos gusta mejor object1 ... Entonces, "asignamos" una nueva variable.

var favoriteObject = object1;

A continuación, por cualquier motivo, decidimos que nos gusta más el objeto 2. Entonces, simplemente hacemos una pequeña reasignación.

favoriteObject = object2;

No le pasó nada al objeto1 ni al objeto2. No hemos cambiado ningún dato en absoluto. Todo lo que hicimos fue reasignar cuál es nuestro objeto favorito. Es importante saber que object2 y favoriteObject están asignados al mismo objeto. Podemos cambiar ese objeto a través de cualquiera de esas variables.

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

OK, ahora veamos primitivas como cadenas por ejemplo

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

De nuevo, elegimos un favorito.

var favoriteString = string1;

Nuestras variables favoriteString y string1 se asignan a 'Hello world'. Ahora, ¿qué pasa si queremos cambiar nuestra cadena favorita? ¿¿¿Lo que sucederá???

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

Uh oh ... lo que ha pasado. No pudimos cambiar string1 cambiando a favoriteString ... ¿Por qué? Porque no cambiamos nuestro objeto de cadena . Todo lo que hicimos fue "RE ASIGNAR" la variable favoriteString a una nueva cadena. Esto esencialmente lo desconectó de la cadena1. En el ejemplo anterior, cuando cambiamos el nombre de nuestro objeto, no asignamos nada. (Bueno, no a la variable en sí , ... sin embargo, asignamos la propiedad de nombre a una nueva cadena.) En cambio, simplemente mutamos el objeto que mantiene las conexiones entre las 2 variables y los objetos subyacentes. (Incluso si hubiéramos querido modificar o mutar el objeto de cadena en , no podríamos haberlo hecho, porque las cadenas son realmente inmutables en JavaScript).

Ahora, a las funciones y a pasar parámetros ... Cuando llamas a una función y pasas un parámetro, lo que estás haciendo esencialmente es una "asignación" a una nueva variable, y funciona exactamente igual que si simplemente te asignaras usando el signo igual (=)

Toma estos ejemplos.

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'

Ahora, lo mismo, pero con una función

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'

Bien, ahora demos algunos ejemplos usando objetos en su lugar ... primero, sin la función.

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

Ahora, lo mismo, pero con una llamada a función

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 lees toda esta publicación, quizás ahora comprendas mejor cómo funcionan las llamadas a funciones en JavaScript. No importa si algo se pasa por referencia o por valor ... Lo que importa es la asignación frente a la mutación.

Cada vez que pasa una variable a una función, está "Asignando" a cualquiera que sea el nombre de la variable del parámetro, al igual que si usara el signo igual (=).

Recuerde siempre que el signo igual (=) significa asignación. Recuerde siempre que pasar un parámetro a una función en JavaScript también significa asignación. Son las mismas y las 2 variables están conectadas exactamente de la misma manera (es decir, no lo son, a menos que cuente que están asignadas al mismo objeto).

El único momento en que "modificar una variable" afecta a una variable diferente es cuando el objeto subyacente está mutado (en cuyo caso no ha modificado la variable, sino el objeto en sí).

No tiene sentido hacer una distinción entre objetos y primitivas, porque funciona de la misma manera que si no tuviera una función y solo utilizara el signo igual para asignar a una nueva variable.

El único problema es cuando el nombre de la variable que pasa a la función es el mismo que el nombre del parámetro de la función. Cuando esto sucede, debe tratar el parámetro dentro de la función como si fuera una variable completamente nueva y privada para la función (porque lo es)

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'

2
Para cualquier programador de C, piense en char *. foo(char *a){a="hello";} no hace nada, pero si lo hace foo(char *a){a[0]='h';a[1]='i';a[2]=0;}, se cambia afuera porque aes una ubicación de memoria pasada por valor que hace referencia a una cadena (matriz de caracteres). Se permite pasar estructuras (similares a los objetos js) por valor en C, pero no se recomienda. JavaScript simplemente impone estas mejores prácticas y oculta los elementos innecesarios y generalmente no deseados ... y seguramente facilita la lectura.
technosaurus

2
Esto es correcto: los términos paso por valor y paso por referencia tienen un significado en el diseño del lenguaje de programación, y esos significados no tienen nada que ver con la mutación de objetos. Se trata de cómo funcionan los parámetros de función.
Puntiagudo

2
Ahora que entiendo que obj1 = obj2 significa que tanto obj1 como obj2 ahora apuntan a la misma ubicación de referencia, y si modifico las partes internas de obj2, hacer referencia a obj1 expondrá las mismas partes internas. ¿Cómo copio un objeto de modo que cuando lo haga, source = { "id":"1"}; copy = source /*this is wrong*/; copy.id="2"esa fuente siga siendo {"id": "1"}?
Machtyn

1
Publiqué otra respuesta con definiciones tradicionales para reducir la confusión. Las definiciones tradicionales de "paso por valor" y "paso por referencia" se definieron en el día de los punteros de memoria antes de la desreferencia automática. Se entendió perfectamente que el valor de una variable de objeto era en realidad la ubicación del puntero de memoria, no el objeto. Aunque su discusión sobre la asignación frente a la mutación quizás sea útil, no es necesario descartar los términos tradicionales ni sus definiciones. La mutación, la asignación, el paso por valor, el paso por referencia, etc. no deben contradecirse entre sí.
C Perkins

¿"Número" es un "inmutable" también?
ebram khalil

72

Considera lo siguiente:

  1. Las variables son punteros a valores en la memoria.
  2. La reasignación de una variable simplemente apunta ese puntero a un nuevo valor.
  3. La reasignación de una variable nunca afectará a otras variables que apuntaban a ese mismo objeto

Entonces, olvídate de "pasar por referencia / valor" , no te obsesiones con "pasar por referencia / valor" porque:

  1. Los términos solo se usan para describir el comportamiento de un lenguaje, no necesariamente la implementación subyacente real. Como resultado de esta abstracción, se pierden detalles críticos que son esenciales para una explicación decente, lo que inevitablemente conduce a la situación actual en la que un solo término no describe adecuadamente el comportamiento real y se debe proporcionar información complementaria
  2. Estos conceptos no se definieron originalmente con la intención de describir JavaScript en particular, por lo que no me siento obligado a usarlos cuando solo aumentan la confusión.

Para responder a su pregunta: se pasan los punteros.


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

Algunos comentarios finales:

  • Es tentador pensar que las reglas especiales imponen primitivas mientras que los objetos no, pero las primitivas son simplemente el final de la cadena de punteros.
  • Como último ejemplo, considere por qué un intento común de borrar una matriz no funciona como se esperaba.


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

Preguntas de seguimiento para crédito adicional;) ​​¿Cómo funciona la recolección de basura? Si ciclo una variable a través de un millón de {'George', 1}valores, pero solo uso uno de ellos a la vez, ¿cómo se gestionan los demás? ¿Y qué sucede cuando asigno una variable al valor de otra variable? ¿Estoy señalando un puntero o señalando al puntero del operando correcto? ¿El var myExistingVar = {"blah", 42}; var obj = myExistingVar;resultado objapunta a {"blah", 42}o hacia myExistingVar?
Michael Hoffmann

@MichaelHoffmann Estas merecen sus propias preguntas SO y probablemente ya hayan sido respondidas mejor de lo que puedo manejar. Dicho esto, 1)ejecuté un perfil de memoria en las herramientas de desarrollo del navegador para una función de bucle como la que describió y vi picos en el uso de la memoria durante todo el proceso de bucle. Esto parecería indicar que se están creando nuevos objetos idénticos en cada iteración del bucle. Cuando las espinas caen repentinamente, el recolector de basura acaba de limpiar un grupo de estos objetos no utilizados.
geg

1
@MichaelHoffmann 2)Con respecto a algo como var a = b, javascript no proporciona un mecanismo para usar punteros y, por lo tanto, una variable nunca puede apuntar a un puntero (como puede hacerlo en C), aunque el motor de javascript subyacente los usa indudablemente. Entonces ... var a = bseñalará a"a la punta del operando correcto"
geg

Hice la pregunta # 1 aquí (específicamente sobre Chrome porque la implementación es probablemente diferente en cada navegador) stackoverflow.com/q/42778439/539997 y todavía estoy tratando de pensar en cómo formular la pregunta # 2. Cualquier ayuda es apreciada.
Michael Hoffmann

1
¡No hay necesidad de olvidarse de "pasar por referencia / valor" ! Estos términos tienen significados históricos que describen exactamente lo que intenta describir. Si desechamos los términos y definiciones históricos y nos volvemos demasiado vagos para aprender lo que significaban originalmente, entonces perdemos la capacidad de comunicarnos de manera efectiva entre generaciones. No habría una buena manera de discutir las diferencias entre diferentes lenguajes y sistemas. En cambio, los nuevos programadores necesitan aprender y comprender los términos tradicionales y por qué y de dónde provienen. De lo contrario, perdemos colectivamente conocimiento y comprensión.
C Perkins

24

Un objeto fuera de una función se pasa a una función dando una referencia al objeto exterior.

Cuando usa esa referencia para manipular su objeto, el objeto externo se ve afectado. Sin embargo, si dentro de la función decidió señalar la referencia a otra cosa, no afectó en absoluto al objeto externo, porque lo único que hizo fue redirigir la referencia a otra cosa.


20

Piénselo así: siempre pasa por valor. Sin embargo, el valor de un objeto no es el objeto en sí, sino una referencia a ese objeto.

Aquí hay un ejemplo, pasando un número (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

Repetir esto con un objeto produce resultados diferentes:

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 ejemplo más:

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

Una explicación muy detallada acerca de copiar, pasar y comparar por valor y por referencia está en este capítulo del libro "JavaScript: la guía definitiva" encontrará .

Antes de dejar el tema de la manipulación de objetos y matrices por referencia, necesitamos aclarar un punto de nomenclatura.

La frase "pasar por referencia" puede tener varios significados. Para algunos lectores, la frase se refiere a una técnica de invocación de función que permite a una función asignar nuevos valores a sus argumentos y tener esos valores modificados visibles fuera de la función. Esta no es la forma en que se usa el término en este libro.

Aquí, queremos decir simplemente que una referencia a un objeto o matriz, no el objeto en sí, se pasa a una función. Una función puede usar la referencia para modificar las propiedades del objeto o elementos de la matriz. Pero si la función sobrescribe la referencia con una referencia a un nuevo objeto o matriz, esa modificación no es visible fuera de la función.

Los lectores familiarizados con el otro significado de este término pueden preferir decir que los objetos y las matrices se pasan por valor, pero el valor que se pasa es en realidad una referencia más que el objeto en sí.


Wow, esto es increíblemente confuso. ¿Quién en su sano juicio definiría un término bien establecido para significar exactamente lo contrario y luego lo usaría de esa manera? No es de extrañar que tantas respuestas aquí sobre esta pregunta estén tan confundidas.
Jörg W Mittag

16

JavaScript siempre es paso por valor ; todo es de tipo de valor.

Los objetos son valores, y las funciones miembro de los objetos son valores en sí mismos (recuerde que las funciones son objetos de primera clase en JavaScript). Además, con respecto al concepto de que todo en JavaScript es un objeto ; esto está mal. Las cadenas, símbolos, números, booleanos, nulos e indefinidos son primitivos .

En ocasiones, pueden aprovechar algunas funciones y propiedades miembro heredadas de sus prototipos base, pero esto es solo por conveniencia. No significa que sean objetos en sí mismos. Pruebe lo siguiente como referencia:

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

En ambas alertas encontrará que el valor no está definido.


12
-1, no siempre se pasa por valor. De MDC: "Si pasa un objeto (es decir, un valor no primitivo, como Array o un objeto definido por el usuario) como parámetro, se pasa una referencia al objeto a la función".
Nick

37
@ Nick: siempre se pasa por valor. Período. Una referencia al objeto se pasa por valor a la función. Eso no pasa por referencia. "Pasar por referencia" casi podría considerarse como pasar la variable en sí, en lugar de su valor; cualquier cambio que la función realice en el argumento (¡incluso reemplazarlo con un objeto completamente diferente!) se reflejaría en la persona que llama. Ese último bit no es posible en JS, porque JS no pasa por referencia , pasa referencias por valor. La distinción es sutil, pero bastante importante para comprender sus limitaciones.
cHao

1
Para futuros apiladores ... Acerca de esta referencia suya: x = "teste"; x.foo = 12;etc. El hecho de que una propiedad no sea persistente no significa que no sea un objeto. Como dice MDN: en JavaScript, casi todo es un objeto. Todos los tipos primitivos, excepto nulo e indefinido, se tratan como objetos. Se les pueden asignar propiedades (las propiedades asignadas de algunos tipos no son persistentes) y tienen todas las características de los objetos. enlace
slacktracer

99
MDN es un wiki editado por el usuario y está mal allí. La referencia normativa es ECMA-262. Consulte S. 8 "El tipo de especificación de referencia", que explica cómo se resuelven las referencias, y también 8.12.5 "[[Put]]", que se utiliza para explicar AssignmentExpression a una referencia y, para la coersión de objetos 9.9 ToObject. Para valores primitivos, Michael ya explicó lo que hace ToObject, como en la especificación. Pero ver también s. 4.3.2 valor primitivo.
Garrett

1
@WonderLand: No, no lo es. Las personas que nunca han podido pasar por referencia pueden nunca entender las diferencias entre pasar por referencia y pasar una referencia por valor. Pero están ahí, y son importantes. No me importa desinformar a las personas solo porque suena más fácil.
cHao

12

En JavaScript, el tipo de valor controla únicamente si ese valor se asignará por copia de valor o por copia de referencia .

Los valores primitivos siempre se asignan / pasan por value-copy :

  • null
  • undefined
  • cuerda
  • número
  • booleano
  • símbolo en ES6

Los valores compuestos siempre se asignan / pasan por copia de referencia

  • objetos
  • matrices
  • función

Por ejemplo

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]

En el fragmento anterior, porque 2es una primitiva escalar, acontiene una copia inicial de ese valor y bse le asigna otra copia del valor. Al cambiar b, de ninguna manera está cambiando el valor a.

Pero ambos cy dson referencias separadas al mismo valor compartido [1,2,3], que es un valor compuesto. Es importante tener en cuenta que ni el cni dmás "posee" el [1,2,3]valor - ambos son iguales sólo referencias a los compañeros del valor. Por lo tanto, cuando se usa cualquiera de las referencias para modificar ( .push(4)) el arrayvalor compartido real en sí, solo afecta al valor compartido, y ambas referencias harán referencia al valor recién modificado [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]

Cuando hacemos la tarea b = [4,5,6], no estamos haciendo absolutamente nada para afectar a dónde atodavía se hace referencia ( [1,2,3]). Para hacer eso, btendría que ser un puntero en alugar de una referencia a array- ¡pero no existe tal capacidad en 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]

Cuando pasamos el argumento a, asigna una copia de la areferencia a x. xy ason referencias separadas que apuntan al mismo [1,2,3]valor. Ahora, dentro de la función, podemos usar esa referencia para mutar el valor en sí ( push(4)). Pero cuando hacemos la asignación x = [4,5,6], esto no afecta de ninguna manera a dónde aapunta la referencia inicial , todavía apunta a (ahora modificado)[1,2,3,4] .

Para pasar efectivamente un valor compuesto (como un array) por value-copy, debe hacer una copia manualmente de él, de modo que la referencia pasada no apunte al original. Por ejemplo:

foo( a.slice() );

Valor compuesto (objeto, matriz, etc.) que se puede pasar por copia de referencia

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

var obj = {
    a: 2
};

foo( obj );

obj.a; // 42

Aquí, objactúa como un contenedor para la propiedad primitiva escalar a. Cuando se pasa a foo(..), se pasa una copia de la objreferencia y se establece en el wrapperparámetro. Ahora podemos usar la wrapperreferencia para acceder al objeto compartido y actualizar su propiedad. Una vez que finalice la función, obj.averá el valor actualizado 42.

Fuente


Primero declara "Los valores compuestos siempre se asignan / pasan por referencia-copia", y luego dice "asigna una copia de la referencia a x". En el caso de lo que llama un "valor compuesto", el valor variable real ES la referencia (es decir, el puntero de memoria). Tal como lo explicó, la referencia se copia ... por lo que se copia el valor de las variables , enfatizando nuevamente que la REFERENCIA ES EL VALOR. Eso significa que JavaScript es paso por valor para todos los tipos. Pasar por valor significa pasar una copia del valor de las variables. No importa que el valor sea una referencia a un objeto / matriz.
C Perkins

Introduce una nueva terminología (valor-copia / referencia-copia) y eso solo hace las cosas más complejas. Solo hay copias, punto. Si pasa una primitiva, pasó una copia de los datos primitivos reales, si pasa un objeto, pasó una copia de la ubicación de la memoria del objeto. Eso es todo lo que necesitas decir. Cualquier cosa más simplemente confunde aún más a las personas.
Scott Marcus el

9

bueno, se trata de 'rendimiento' y 'velocidad' y en la simple palabra 'gestión de memoria' en un lenguaje de programación.

en javascript podemos poner valores en dos capas: type1 - objectsy type2 -todos los demás tipos de valor como string& boolean& etc.

si imagina la memoria como los cuadros a continuación, que en cada uno de ellos solo se puede guardar un valor tipo2

ingrese la descripción de la imagen aquí

cada valor type2 (verde) es un cuadrado único, mientras que un valor type1 (azul) es un grupo de ellos :

ingrese la descripción de la imagen aquí

El punto es que si desea indicar un valor de tipo 2, la dirección es simple, pero si desea hacer lo mismo para el valor de tipo 1, ¡eso no es nada fácil! :

ingrese la descripción de la imagen aquí

y en una historia más complicada:

ingrese la descripción de la imagen aquí

así que aquí las referencias pueden rescatarnos: ingrese la descripción de la imagen aquí

mientras que la flecha verde aquí es una variable típica, la púrpura es una variable de objeto, así que debido a que la flecha verde (variable típica) tiene solo una tarea (y eso indica un valor típico) no necesitamos separar su valor de así que movemos la flecha verde con el valor de eso donde quiera que vaya y en todas las tareas, funciones, etc.

pero no podemos hacer lo mismo con la flecha morada, es posible que queramos mover la celda 'John' aquí o muchas otras cosas ..., por lo que la flecha morada se mantendrá en su lugar y solo las flechas típicas que se le asignaron se moverán ...

Una situación muy confusa es donde no puede darse cuenta de cómo cambia su variable referenciada, echemos un vistazo a un muy buen ejemplo:

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

ingrese la descripción de la imagen aquí ingrese la descripción de la imagen aquí


8

Esta es una explicación más para pasar por valor y pasar por referencia (JavaScript). En este concepto, están hablando de pasar la variable por referencia y pasar la variable por referencia.

Pasar por valor (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
  • se aplica a todos los tipos primitivos en JavaScript (cadena, número, booleano, indefinido y nulo).
  • a se le asigna una memoria (digamos 0x001) yb crea una copia del valor en la memoria (digamos 0x002).
  • Por lo tanto, cambiar el valor de una variable no afecta a la otra, ya que ambas residen en dos ubicaciones diferentes.

Pase por referencia (objetos)

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" }
  • El motor de JavaScript asigna el objeto a la variable cy señala algo de memoria, digamos (0x012).
  • Cuando d = c, en este paso dapunta a la misma ubicación (0x012).
  • Cambiar el valor de cualquier cambio de valor tanto para la variable.
  • Las funciones son objetos.

Caso especial, pase por referencia (objetos)

c = {"name" : "jane"};
console.log(c); // { "name" : "jane" }
console.log(d); // { "name" : "doe" }
  • El operador igual (=) configura un nuevo espacio de memoria o dirección

En su llamado caso especial, no es el operador de asignación el que causa la asignación del espacio de memoria, es el objeto literal en sí. La notación de corchetes provoca la creación de un nuevo objeto. La propiedad cse establece en una copia de la referencia del nuevo objeto.
georgeawg

6

compartir lo que sé de referencias en JavaScript

En JavaScript, al asignar un objeto a una variable, el valor asignado a la variable es una referencia al objeto:

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


1
Esta es una respuesta demasiado simplista que no dice nada que las respuestas anteriores no hayan explicado mejor. Estoy confundido acerca de por qué llama a las matrices como un caso especial.
Quentin

1
"los objetos se almacenan como referencias " es engañoso. Lo que creo que quiere decir es que cuando se asigna un objeto a una variable, el valor asignado a la variable es una referencia al objeto.
RobG

Esto no soluciona el problema de actualizar un objeto dentro de una función que no actualiza el objeto fuera de la función. Esa es la imagen completa donde parece funcionar como valores en lugar de referencia. Por lo tanto -1
amaster

@amaster ¡Gracias por señalar eso! ¿Puedes sugerir una edición, por favor?
Xameeramir

Jaja, lo intenté ... mi edición sugerida cambió demasiado y no estaba permitido
amaster

4

¡¡Semántica!! Establecer definiciones concretas necesariamente hará que algunas respuestas y comentarios sean incompatibles, ya que no describen la misma cosa incluso cuando se usan las mismas palabras y frases, pero es fundamental superar la confusión (especialmente para los nuevos programadores).

En primer lugar, hay múltiples niveles de abstracción que no todos parecen comprender. Los programadores más nuevos que han aprendido lenguajes de 4ta o 5ta generación pueden tener dificultades para comprender conceptos familiares para los ensambladores o programadores en C que no son eliminados por punteros a punteros a punteros. Pasar por referencia no significa simplemente la capacidad de cambiar un objeto referenciado usando una variable de parámetro de función.

Variable : concepto combinado de un símbolo que hace referencia a un valor en una ubicación particular de la memoria. Este término generalmente está demasiado cargado para usarse solo en la discusión de detalles.

Símbolo : cadena de texto utilizada para referirse a la variable (es decir, el nombre de la variable).

Valor : bits particulares almacenados en la memoria y referenciados mediante el símbolo de la variable.

Ubicación de la memoria : donde se almacena el valor de una variable. (La ubicación en sí está representada por un número separado del valor almacenado en la ubicación).

Parámetro de función : Variable declarada en una definición de función, utilizada para hacer referencia a variables pasadas a la función.

Argumento de la función : Variable fuera de la función que el llamante pasa a la función.

Variable de objeto : Variable cuyo valor subyacente básico no es el "objeto" en sí mismo, sino que su valor es un puntero (valor de ubicación de memoria) a otra ubicación en la memoria donde se almacenan los datos reales del objeto. En la mayoría de los lenguajes de generación superior, el aspecto de "puntero" está efectivamente oculto por la referencia automática en varios contextos.

Variable primitiva : Variable cuyo valor ES el valor real. Incluso este concepto puede ser complicado por el auto-boxeo y los contextos similares a objetos de varios idiomas, pero la idea general es que el valor de la variable ES el valor real representado por el símbolo de la variable en lugar de un puntero a otra ubicación de memoria.

Los argumentos y parámetros de la función no son lo mismo. Además, el valor de una variable no es el objeto de la variable (como ya han señalado varias personas, pero aparentemente ignorado). Estas distinciones son críticas para la comprensión adecuada.

Pass-by-value o Call-by-sharing (para objetos): El valor del argumento de la función es COPIADO a otra ubicación de memoria a la que hace referencia el símbolo del parámetro de la función (independientemente de si está en la pila o en el montón). En otras palabras, el parámetro de función recibió una copia del valor del argumento pasado ... Y (crítico) el valor del argumento NUNCA SE ACTUALIZA / ALTERA / CAMBIA por la función de llamada. Recuerde, el valor de una variable de objeto NO es el objeto en sí, sino que es el puntero al objeto, por lo que pasar una variable de objeto por valor copia el puntero a la variable del parámetro de función. El valor del parámetro de función apunta exactamente al mismo objeto en la memoria. Los datos del objeto en sí pueden modificarse directamente a través del parámetro de función, PERO el valor del argumento de la función NUNCA SE ACTUALIZA, por lo que continuará apuntando a mismoobjeto durante e incluso después de la llamada a la función (incluso si los datos de su objeto fueron alterados o si el parámetro de la función tiene asignado un objeto completamente diferente). Es incorrecto concluir que el argumento de la función se pasó por referencia solo porque el objeto al que se hace referencia es actualizable a través de la variable del parámetro de la función.

Llamar / Pasar por referencia : el valor del argumento de la función puede / será actualizado directamente por el parámetro de función correspondiente. Si ayuda, el parámetro de función se convierte en un "alias" efectivo para el argumento: se refieren efectivamente al mismo valor en la misma ubicación de memoria. Si un argumento de función es una variable de objeto, la capacidad de cambiar los datos del objeto no es diferente del caso de paso por valor ya que el parámetro de función todavía apuntará al mismo objeto que el argumento. Pero en el caso de la variable de objeto, si el parámetro de función se establece en un objeto completamente diferente, entonces el argumento también apuntará al objeto diferente; esto no sucede en el caso de pasar por valor.

JavaScript no pasa por referencia. Si lees atentamente, te darás cuenta de que todas las opiniones contrarias no entienden lo que se entiende por paso por valor y concluyen falsamente que la capacidad de actualizar los datos de un objeto a través del parámetro de función es sinónimo de "paso por valor".

Copia / copia de objeto: se crea un nuevo objeto y se copian los datos del objeto original. Esto puede ser una copia profunda o una copia superficial, pero el punto es que se crea un nuevo objeto. Crear una copia de un objeto es un concepto separado del paso por valor. Algunos lenguajes distinguen entre objetos de clase y estructuras (o similares), y pueden tener un comportamiento diferente para pasar variables de los diferentes tipos. Pero JavaScript no hace nada como esto automáticamente al pasar variables de objeto. Pero la ausencia de clonación automática de objetos no se traduce en paso por referencia.


4

JavaScript pasa tipos primitivos por valor y tipos de objetos por referencia

Ahora, a la gente le gusta discutir sin parar acerca de si "pasar por referencia" es la forma correcta de describir lo que Java et al. En realidad hacer. El punto es este:

  1. Pasar un objeto no copia el objeto.
  2. Un objeto pasado a una función puede tener sus miembros modificados por la función.
  3. Un valor primitivo pasado a una función no puede ser modificado por la función. Se hace una copia.

En mi libro eso se llama pasar por referencia.

- Brian Bi - ¿Qué lenguajes de programación se pasan por referencia?


Actualizar

Aquí hay una refutación a esto:

No hay "pasar por referencia" disponible en JavaScript.


@Amy Porque eso describe pasar por valor, no pasar por referencia. Esta respuesta es buena y muestra la diferencia: stackoverflow.com/a/3638034/3307720
nasch

@nasch Entiendo la diferencia. # 1 y # 2 describen la semántica de paso por referencia. # 3 describe la semántica de paso por valor.
Amy

@Amy 1, 2 y 3 son consistentes con el paso por valor. Para pasar por referencia, también necesitaría 4: asignar la referencia a un nuevo valor dentro de la función (con el operador =) también reasigna la referencia fuera de la función. Este no es el caso con Javascript, por lo que pasa exclusivamente por valor. Al pasar un objeto, pasa un puntero al objeto y pasa ese puntero por valor.
nasch

En general, eso no es lo que se entiende por "paso por referencia". Has satisfecho mi consulta y no estoy de acuerdo contigo. Gracias.
Amy

"En mi libro eso se llama pasar por referencia". - En cada libro de compilación, libro de intérpretes, libro de teoría del lenguaje de programación y libro de ciencias de la computación jamás escrito, no lo es.
Jörg W Mittag

3

Mi manera simple de entender esto ...

  • Al llamar a una función, está pasando el contenido (referencia o valor) de las variables de argumento, no las variables en sí.

    var var1 = 13;
    var var2 = { prop: 2 };
    
    //13 and var2's content (reference) are being passed here
    foo(var1, var2); 
  • Dentro de la función, variables de parámetros inVar1y inVar2recibir los contenidos que se pasan.

    function foo(inVar1, inVar2){
        //changing contents of inVar1 and inVar2 won't affect variables outside
        inVar1 = 20;
        inVar2 = { prop: 7 };
    }
  • Desde que inVar2recibió la referencia de { prop: 2 }, puede cambiar el valor de la propiedad del objeto.

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

3

Pasar argumentos a una función en JavaScript es análogo a pasar parámetros por valor de puntero 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;
}

1
No creo que este sea el caso en JavaScript: `` `javascript var num = 5;
Danail Nachev

@DanailNachev: Si bien eso puede ser técnicamente cierto, la diferencia solo es observable para objetos mutables que no son primitivas ECMAScript.
Jörg W Mittag

3

Para los abogados de lenguaje de programación, revisé las siguientes secciones de ECMAScript 5.1 (que es más fácil de leer que la última edición) y llegué a preguntar en la lista de correo de ECMAScript.

TL; DR : Todo pasa por valor, pero las propiedades de los Objetos son referencias, y la definición de Objeto carece de forma espeluznante en el estándar.

Construcción de listas de argumentos

La Sección 11.2.4 "Listas de argumentos" dice lo siguiente al producir una lista de argumentos que consta de solo 1 argumento:

La producción ArgumentList: AssignmentExpression se evalúa de la siguiente manera:

  1. Deje que ref sea el resultado de evaluar AssignmentExpression.
  2. Deje arg ser GetValue (ref).
  3. Devuelve una lista cuyo único elemento es arg.

La sección también enumera casos en los que la lista de argumentos tiene 0 o> 1 argumentos.

Por lo tanto, todo se pasa por referencia.

Acceso a las propiedades del objeto

Sección 11.2.1 "Accesores de propiedades"

La producción MemberExpression: MemberExpression [Expression] se evalúa de la siguiente manera:

  1. Deje que baseReference sea el resultado de evaluar MemberExpression.
  2. Deje que baseValue sea GetValue (baseReference).
  3. Deje que propertyNameReference sea el resultado de evaluar Expression.
  4. Deje que propertyNameValue sea GetValue (propertyNameReference).
  5. Llame a CheckObjectCoercible (baseValue).
  6. Deje que propertyNameString sea ToString (propertyNameValue).
  7. Si la producción sintáctica que se está evaluando está contenida en un código de modo estricto, que estricto sea verdadero, de lo contrario, que estricto sea falso.
  8. Devuelve un valor de tipo Referencia cuyo valor base es baseValue y cuyo nombre referenciado es propertyNameString, y cuyo indicador de modo estricto es estricto.

Por lo tanto, las propiedades de los objetos siempre están disponibles como referencia.

En referencia

En la sección 8.7, "Tipo de especificación de referencia", se describe que las referencias no son tipos reales en el idioma, solo se usan para describir el comportamiento de los operadores de eliminación, typeof y asignación.

Definición de "objeto"

En la edición 5.1 se define que "Un objeto es una colección de propiedades". Por lo tanto, podemos inferir que el valor del objeto es la colección, pero en cuanto a cuál es el valor de la colección está mal definido en la especificación y requiere un poco de esfuerzo para comprenderlo.


Nunca deja de sorprenderme cuántas personas se confunden por las distinciones entre argumentos pasados ​​por valor, argumentos pasados ​​por referencia, operaciones en objetos completos y operaciones en sus propiedades. En 1979, no obtuve mi título en ciencias de la computación, elegí agregar 15 horas más o menos de asignaturas optativas de CS a mi programa de MBA. Sin embargo, pronto se hizo evidente para mí que mi comprensión de estos conceptos era al menos tan buena como la de cualquiera de mis colegas que tenían títulos en ciencias de la computación o matemáticas. Estudie Ensamblador, y quedará bastante claro.
David A. Gray

3

Los documentos de MDN lo explican claramente, sin ser demasiado detallado:

Los parámetros de una llamada a función son los argumentos de la función . Los argumentos se pasan a las funciones por valor . Si la función cambia el valor de un argumento, este cambio no se refleja globalmente o en la función de llamada. Sin embargo, las referencias a objetos también son valores, y son especiales: si la función cambia las propiedades del objeto referido, ese cambio es visible fuera de la función, (...)

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


1

En un lenguaje de bajo nivel, si desea pasar una variable por referencia, debe usar una sintaxis específica en la creación de la función:

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

El &agees una referencia a myAge, pero si desea el valor tiene que convertir la referencia, utilizando*age .

Javascript es un lenguaje de alto nivel que realiza esta conversión por usted. Entonces, aunque los objetos se pasan por referencia, el lenguaje convierte el parámetro de referencia al valor. No necesita usar &, en la definición de la función, para pasarlo por referencia, ni* , en el cuerpo de la función, para convertir la referencia al valor, JS lo hace por usted.

Es por eso que cuando intenta cambiar un objeto dentro de una función, al reemplazar su valor (es decir age = {value:5}), el cambio no persiste, pero si cambia sus propiedades (es decir,age.value = 5 ), lo hace.

Aprende más


1

He leído estas respuestas varias veces, pero REALMENTE no lo entendí hasta que aprendí acerca de la definición técnica de "Llamar al compartir", como lo llama Barbara Liskov

La semántica de la llamada al compartir difiere de la llamada por referencia en que las asignaciones a los argumentos de la función dentro de la función no son visibles para la persona que llama (a diferencia de la semántica de referencia) [cita requerida], por lo que, por ejemplo, si se pasó una variable, no es posible para simular una asignación en esa variable en el alcance de la persona que llama. Sin embargo, dado que la función tiene acceso al mismo objeto que la persona que llama (no se realiza ninguna copia), las mutaciones a esos objetos, si los objetos son mutables, dentro de la función son visibles para la persona que llama, que puede parecer diferente de la llamada por valor semántica. Las mutaciones de un objeto mutable dentro de la función son visibles para la persona que llama porque el objeto no se copia ni se clona, ​​se comparte.

Es decir, las referencias de parámetros pueden modificarse si va y accede al valor del parámetro en sí. Por otro lado, la asignación a un parámetro desaparecerá después de la evaluación, y la persona que llama la función no puede acceder a ella.


No, si un objeto es mutable o no no es realmente el problema. Todo siempre se pasa por valor. Solo depende de lo que esté pasando (un valor o una referencia). Mira esto .
Scott Marcus el

Lo que está describiendo es pasar una referencia BY-VALUE. No hay razón para introducir una nueva terminología.
Sanjeev

1

La forma más simple

// Copy JS object without reference
var first = {"a":"value1","b":"value2"};
var clone = JSON.parse( JSON.stringify( first ) ); 

var second = ["a","b","c"];
var clone = JSON.parse( JSON.stringify( second ) ); 

JSON.parse( JSON.stringify( obj ) )es una forma horrible de clonar objetos profundos. No solo no solo es lento, sino que también puede causar la pérdida de datos.
D. Pardal

0

He encontrado que el método extendido de la biblioteca Underscore.js es muy útil cuando quiero pasar un objeto como un parámetro que puede modificarse o reemplazarse por completo.

function replaceOrModify(aObj) {
  if (modify) {

    aObj.setNewValue('foo');

  } else {

   var newObj = new MyObject();
   // _.extend(destination, *sources) 
   _.extend(newObj, aObj);
  }
}

0

La explicación más sucinta que encontré fue en la guía de estilo de AirBNB :

  • Primitivas : cuando accedes a un tipo primitivo trabajas directamente en su valor

    • cuerda
    • número
    • booleano
    • nulo
    • indefinido

P.ej:

var foo = 1,
    bar = foo;

bar = 9;

console.log(foo, bar); // => 1, 9
  • Complejo : cuando accede a un tipo complejo, trabaja en una referencia a su valor

    • objeto
    • formación
    • función

P.ej:

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

bar[0] = 9;

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

Es decir, los tipos primitivos efectivamente se pasan por valor, y los tipos complejos se pasan por referencia.


No, todo se pasa siempre por valor. Solo depende de lo que esté pasando (un valor o una referencia). Mira esto .
Scott Marcus el

-1

Yo diría que es pasar por copia.

Tenga en cuenta que los argumentos y los objetos variables son objetos creados durante el contexto de ejecución creado al comienzo de la invocación de la función, y su valor / referencia real pasado a la función simplemente se almacena en estos argumentos + objetos variables.

En términos simples, para los tipos primitivos, los valores se copian al comienzo de la llamada a la función, para el tipo de objeto, se copia la referencia.


1
"pasar por copia" === pasar por valor
Scott Marcus

-1

Hay un poco de debate sobre el uso del término "paso por referencia" en JavaScript aquí , pero para responder a su pregunta:

Un objeto se pasa automáticamente por referencia, sin la necesidad de indicarlo específicamente

(Del artículo mencionado anteriormente).


77
El artículo vinculado ya no incluye esas declaraciones y evita el uso de "pasar por referencia" por completo.
C Perkins

El valor es una referencia

-2

Una manera fácil de determinar si algo es "pasar por referencia" es si puede escribir una función de "intercambio". Por ejemplo, en C, puedes hacer:

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

Si no puede hacer el equivalente de eso en JavaScript, no es "pasar por referencia".


21
Esto no es realmente pasar por referencia. Está pasando punteros a la función, y esos punteros se pasan por valor. Un mejor ejemplo sería C ++ 's & operator o la palabra clave "ref" de C #, ambas verdaderamente pasan por referencia.
Matt Greer

Aún más fácil es que todo se pasa por valor en JavaScript.
Scott Marcus el


-3
  1. tipo de variable primitiva como cadena, el número siempre se pasa como pasa por valor.
  2. Array and Object se pasa como pasar por referencia o pasar por valor basado en estas dos condiciones.

    • si está cambiando el valor de ese objeto o matriz con un nuevo objeto o matriz, entonces se pasa por valor.

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

    aquí está asignando un nuevo objeto o matriz al anterior. no está cambiando el valor de la propiedad del objeto antiguo. por lo tanto, se pasa por valor.

    • si está cambiando el valor de una propiedad de un objeto o matriz, entonces se pasa por Referencia.

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

    aquí está cambiando el valor de una propiedad del objeto antiguo. No está asignando un nuevo objeto o matriz al anterior. Por lo tanto, se pasa 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

1
El operador de asignación no debe confundirse con una llamada de función. Cuando asigna datos nuevos a una variable existente, el recuento de referencia de los datos antiguos disminuye y los datos nuevos se asocian con la variable anterior. Básicamente, la variable termina apuntando a los nuevos datos. Lo mismo es cierto para las variables de propiedad. Como estas asignaciones no son llamadas a funciones, no tienen nada que ver con el paso por valor o el paso por referencia.
John Sonderson

1
No, todo se pasa siempre por valor. Solo depende de lo que esté pasando (un valor o una referencia). Mira esto .
Scott Marcus el

-3
  1. Las primitivas (número, booleano, etc.) se pasan por valor.
    • Las cadenas son inmutables, por lo que realmente no les importa.
  2. Los objetos se pasan por referencia (la referencia se pasa por valor).

No, todo se pasa siempre por valor. Solo depende de lo que esté pasando (un valor o una referencia). Mira esto .
Scott Marcus el

Su segunda afirmación se contradice a sí misma.
Jörg W Mittag

-5

Los valores simples dentro de las funciones no cambiarán esos valores fuera de la función (se pasan por valor), mientras que los complejos sí (se pasan por referencia).

function willNotChange(x) {

    x = 1;
}

var x = 1000;

willNotChange(x);

document.write('After function call, x = ' + x + '<br>'); // Still 1000

function willChange(y) {

    y.num = 2;
}

var y = {num: 2000};

willChange(y);
document.write('After function call y.num = ' + y.num + '<br>'); // Now 2, not 2000

eso es ridículo, y cambiará debido al alcance del nivel funcional, se levanta no porque se pasa por referencia.
Parijat Kalia

No, todo se pasa siempre por valor. Solo depende de lo que esté pasando (un valor o una referencia). Mira esto .
Scott Marcus el
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.