Pregunta ¿JavaScript es un lenguaje de paso por referencia o de valor por paso?


Los tipos primitivos (Número, Cadena, etc.) se pasan por valor, pero los Objetos son desconocidos, porque pueden pasarse 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 para el objeto contiene el objeto mismo).

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


1137
2018-02-05 21:23


origen


Respuestas:


Es interesante en Javascript. Considera 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 fue puro pasar por valor, entonces cambiar obj1.item no tendría ningún efecto en el obj1 fuera de la función.
  • Si fue puro pase por referencia, entonces todo habría cambiado. num sería 100y obj2.item leería "changed".

En cambio, la situación es que el elemento pasado se pasa por valor. Pero el artículo que se pasa por valor es 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 num y obj2), eso no afectará al elemento que se introdujo en el parámetro. Pero si cambias el INTERNOS del parámetro, que propagará la copia de seguridad (como con obj1)


1359
2018-03-15 16:38



Siempre pasa por valor, pero para objetos el valor de la variable es una referencia. Por eso, cuando pasas un objeto y cambias su miembros, esos cambios persisten fuera de la función. Esto lo hace Mira como pasar por referencia. Pero si realmente cambias el valor de la variable de objeto verás que el cambio no persiste, demostrando que realmente pasa por valor.

Ejemplo:

function changeObject(x) {
  x = {member:"bar"};
  alert("in changeObject: " + x.member);
}

function changeMember(x) {
  x.member = "bar";
  alert("in changeMember: " + x.member);
}

var x = {member:"foo"};

alert("before changeObject: " + x.member);
changeObject(x);
alert("after changeObject: " + x.member); /* change did not persist */

alert("before changeMember: " + x.member);
changeMember(x);
alert("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

376
2018-02-05 21:37



La variable no "retiene" el objeto, contiene una referencia. Puede asignar esa referencia a otra variable, 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 tiene una variable pasada como parámetro, lo que sería posible si JS soportara pasar por referencia.


132
2017-08-04 11:06



Mis 2 centavos ... Esta es la forma en que lo entiendo. (No dude en corregirme si estoy equivocado)

Es hora de descartar todo lo que sabes 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 versus la asignación de los parámetros pasados ​​a una función.

De acuerdo, déjame hacer lo mejor 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 object1 better ... Entonces, "asignamos" una nueva variable.

var favoriteObject = object1;

A continuación, por cualquier razón, decidimos que nos gusta mejor el objeto 2. Entonces, simplemente hacemos una pequeña reasignación.

favoriteObject = object2;

No pasó nada con object1 o object2. 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 primitivos como cadenas, por ejemplo

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

Nuevamente, escogemos un favorito.

var favoriteString = string1;

Ambas nuestras variables favoritas String y string1 están asignadas a 'Hola mundo'. Ahora, ¿y 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 ... ¿Qué ha pasado? No pudimos cambiar string1 cambiando FavoriteString ... ¿Por qué? porque las cadenas son inmutables y no las mudamos. Todo lo que hicimos fue "RE ASSIGN" favoritoString a una nueva cadena. Esto esencialmente lo desconectó de string1. En el ejemplo anterior, cuando renombramos nuestro objeto, no asignamos nada. (Bueno, en realidad ... lo hicimos, asignamos la propiedad del nombre a una nueva cadena). En cambio, simplemente mutamos el objeto que mantiene las conexiones entre las 2 variables y los objetos subyacentes.

Ahora, a las funciones y a los parámetros que pasan ... Cuando llama a una función y pasa un parámetro, lo que básicamente está haciendo es "asignar" a una nueva variable, y funciona exactamente igual que si simplemente le asignara 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'

OK, ahora vamos a dar 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
otherObj = {
    firstName: 'Jack',
    lastName: 'Frost'
};

// Now, otherObj and myObject are assigned to 2 very different objects
// And mutating one object no longer mutates the other
console.log(myObject.firstName); // Logs 'Sue'
console.log(otherObj.firstName); // Logs 'Jack';

Ahora, lo mismo, pero con una llamada de 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 no longer mutates 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

De acuerdo, si lees esta publicación completa, quizás ahora comprendas mejor cómo funcionan las llamadas a función 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 de parámetro, como si hubiera usado el signo igual (=).

Recuerde siempre que el signo igual (=) significa asignación. Recuerde siempre que pasar un parámetro a una función también significa asignación. Son lo mismo y las 2 variables están conectadas exactamente de la misma manera.

El único momento en que la modificación de una variable afecta a una variable diferente es cuando el objeto subyacente está mutado.

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

El único problema es cuando el nombre de la variable que pasas 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 nueva variable 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'

84
2017-09-26 04:06



Considera lo siguiente:

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

Asi que, olvidarse 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 real subyacente. Como resultado de esta abstracción, se pierden los detalles críticos que son esenciales para una explicación decente, lo que inevitablemente lleva a la situación actual en la que un solo término no describe adecuadamente el comportamiento real y se debe proporcionar información adicional.
  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 indicadores.


// 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 primitivas se hacen cumplir por reglas especiales mientras objetos no lo son, pero los primitivos 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

57
2018-06-02 23:04



El objeto fuera de una función pasa a una función al hacer referencia al objeto externo. Cuando usas esa referencia para manipular su objeto, el objeto exterior se ve afectado. Sin embargo, si dentro de la función decidiste señalar la referencia a otra cosa, no afectabas al objeto en absoluto, porque todo lo que hiciste fue redirigir la referencia a otra cosa.


23
2018-02-13 11:00



Piénselo de esta manera: 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 diferentes resultados:

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}

16
2018-02-05 21:49



Javascript es siempre pass-by-value, todo es de tipo de valor Los objetos son valores, 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 primitivas. En ocasiones pueden aprovechar algunas funciones y propiedades de los miembros heredadas de sus prototipos básicos, 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.


14
2017-12-30 02:20



En JavaScript, el tipo del valor solamente controla si ese valor será asignado por valor-copia o por copia de referencia.

Los valores primitivos siempre se asignan / pasan por valor-copia:

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

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

  • 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 de arriba, porque 2 es un primitivo escalar, a tiene una copia inicial de ese valor, y b se le asigna otra copia del valor. Al cambiar b, de ninguna manera está cambiando el valor en a.

Sino ambos c y d son referencias separadas para el mismo valor compartido [1,2,3], que es un valor compuesto. Es importante señalar que ni c ni d más "posee" el [1,2,3] valor: ambos son referencias iguales iguales al valor. Entonces, cuando se usa cualquiera de las referencias para modificar (.push(4)) el verdadero compartido array valor en sí mismo, está afectando solo el valor compartido, y ambas referencias harán referencia al valor recientemente 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 dónde a sigue haciendo referencia ([1,2,3]) Para hacer eso, b tendría que ser un puntero a a en lugar de una referencia a la 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 la discusión a, asigna una copia del a referencia a x. x y a son referencias separadas que apuntan a la misma [1,2,3] valor. Ahora, dentro de la función, podemos usar esa referencia para mutar el valor mismo (push(4)) Pero cuando hacemos la tarea x = [4,5,6], esto no afecta en modo alguno a la referencia inicial a está apuntando - todavía apunta a la (ahora modificada) [1,2,3,4] valor.

Para pasar efectivamente un valor compuesto (como un array) mediante la copia de valor, debe hacer una copia de manera manual, 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 referencia-copia

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

var obj = {
    a: 2
};

foo( obj );

obj.a; // 42

Aquí, obj actúa como un contenedor para la propiedad escalar primitiva a. Cuando pasó a foo(..), una copia de obj la referencia se pasa y se establece en wrapperparámetro. Ahora podemos usar el wrapper referencia para acceder al objeto compartido y actualizar su propiedad. Después de que la función termine, obj.a verá el valor actualizado 42.

Fuente


12
2018-05-12 22:20