parameter - pass by reference javascript
¿Javascript pasa por referencia? (10)
¿Pasa Javascript por referencias o pasa por valores? Aquí hay un ejemplo de Javascript: The Good Parts . Estoy muy confundido acerca de my
parámetro para la función de rectángulo. En realidad, es undefined
y redefinido dentro de la función. No hay referencia original. Si lo quito del parámetro de función, la función de área interna no puede acceder a ella.
¿Es un cierre? Pero no se devuelve ninguna función.
var shape = function (config) {
var that = {};
that.name = config.name || "";
that.area = function () {
return 0;
};
return that;
};
var rectangle = function (config, my) {
my = my || {};
my.l = config.length || 1;
my.w = config.width || 1;
var that = shape(config);
that.area = function () {
return my.l * my.w;
};
return that;
};
myShape = shape({
name: "Unhnown"
});
myRec = rectangle({
name: "Rectangle",
length: 4,
width: 6
});
console.log(myShape.name + " area is " + myShape.area() + " " + myRec.name + " area is " + myRec.area());
Al igual que con C, en última instancia, todo se pasa por valor. A diferencia de C, no se puede realizar una copia de seguridad y pasar la ubicación de una variable, ya que no tiene referencias solo de punteros.
Y las referencias que tiene son todas a objetos, no variables. Hay varias formas de lograr el mismo resultado, pero deben hacerse a mano, no solo agregando una palabra clave en el sitio de la llamada o la declaración.
En el interés de crear un ejemplo simple que use const ...
const myRef = { foo: ''bar'' };
const myVal = true;
function passes(r, v) {
r.foo = ''baz'';
v = false;
}
passes(myRef, myVal);
console.log(myRef, myVal); // Object {foo: "baz"} true
En términos prácticos, Alnitak es correcto y lo hace fácil de entender, pero en última instancia en JavaScript, todo se pasa por valor.
¿Cuál es el "valor" de un objeto? Es la referencia del objeto.
Cuando pasa un objeto, obtiene una copia de este valor (de ahí la "copia de una referencia" que describió Alnitak). Si cambia este valor, no cambia el objeto original, está cambiando su copia de esa referencia.
JavaScript es pasar por valor. Para primitivas, se pasa el valor de primitiva. Para los Objetos, se pasa el "valor" de referencia del Objeto.
Ejemplo con objeto:
var f1 = function(inputObject){
inputObject.a=2;
}
var f2 = function(){
var inputObject={"a":1};
f1(inputObject);
console.log(inputObject.a);
}
llamar a f2 da como resultado la impresión de un valor "a" como 2 en lugar de 1, a medida que se pasa la referencia y se actualiza el valor "a" en la referencia.
Ejemplo con primitivo:
var f1 = function(a){
a=2;
}
var f2 = function(){
var a =1;
f1(a);
console.log(a);
}
si se llama f2, se imprime el valor "a" como 1.
Las primitivas se pasan por valor, los objetos se pasan por "copia de una referencia".
Específicamente, cuando pasa un objeto (o matriz) está pasando (invisiblemente) una referencia a ese objeto, y es posible modificar el contenido de ese objeto, pero si intenta sobrescribir la referencia no afectará la copia de la referencia mantenida por el llamante, es decir, la propia referencia se pasa por valor:
function replace(ref) {
ref = {}; // this code does _not_ affect the object passed
}
function update(ref) {
ref.key = ''newvalue''; // this code _does_ affect the _contents_ of the object
}
var a = { key: ''value'' };
replace(a); // a still has its original value - it''s unmodfied
update(a); // the _contents_ of ''a'' are changed
Las variables de javascript "globales" son miembros del objeto de ventana. Puede acceder a la referencia como miembro del objeto de ventana.
var v = "initialized";
function byref(ref) {
window[ref] = "changed by ref";
}
byref((function(){for(r in window){if(window[r]===v){return(r);}}})());
// could also be called like... byref(''v'');
console.log(v); // outputs changed by ref
Tenga en cuenta que el ejemplo anterior no funcionará para las variables declaradas dentro de una función.
Los argumentos de la función se pasan por valor o por compartir, ¡pero nunca NUNCA por referencia en Javascript!
Call-by-Value
Los tipos primitivos se pasan por valor:
var num = 123, str = "foo";
function f(num, str) {
num += 1;
str += "bar";
console.log("inside of f:", num, str);
}
f(num, str);
console.log("outside of f:", num, str);
Las reasignaciones dentro de un ámbito de función no son visibles en el ámbito circundante.
Esto también se aplica a las String
, que son un tipo de datos compuestos y aún así inmutables:
var str = "foo";
function f(str) {
str[0] = "b"; // doesn''t work, because strings are immutable
console.log("inside of f:", str);
}
f(str);
console.log("outside of f:", str);
Call-by-Sharing
Objetos, es decir, todos los tipos que no son primitivos se pasan compartiendo. Una variable que contiene una referencia a un objeto en realidad contiene simplemente una copia de esta referencia. Si Javascript persiguiera una estrategia de evaluación llamada por referencia, la variable mantendría la referencia original. Esta es la diferencia crucial entre compartir y referencia.
¿Cuáles son las consecuencias prácticas de esta distinción?
var o = {x: "foo"}, p = {y: 123};
function f(o, p) {
o.x = "bar"; // mutation
p = {x: 456}; // reassignment
console.log("o inside of f:", o);
console.log("p inside of f:", p);
}
f(o, p);
console.log("o outside of f:", o);
console.log("p outside of f:", p);
Medios mutantes para modificar ciertas propiedades de un Object
existente. La copia de referencia a la que está vinculada una variable y que se refiere a este objeto sigue siendo la misma. Las mutaciones son así visibles en el alcance de la persona que llama.
Reasignar significa reemplazar la copia de referencia vinculada a una variable. Como solo es una copia, las otras variables que contienen una copia de la misma referencia no se verán afectadas. Por lo tanto, las reasignaciones no son visibles en el alcance de la persona que llama, como lo sería con una estrategia de evaluación llamada por referencia.
Más información sobre estrategias de evaluación en ecmascript.
Mis 2 centavos .... Es irrelevante si Javascript pasa parámetros por referencia o valor. Lo que realmente importa es la asignación vs mutación.
Escribí una explicación más larga y detallada aquí ( ¿Es JavaScript un lenguaje de paso por referencia o de paso por valor? )
Cuando pasa algo (ya sea que sea un objeto o un primitivo), todo javascript asigna una nueva variable mientras está dentro de la función ... igual que usar el signo igual (=)
La forma en que se comporta ese parámetro dentro de la función es exactamente la misma que se comportaría si simplemente asignara una nueva variable utilizando el signo igual. Tome estos ejemplos simples.
var myString = ''Test string 1'';
// Assignment - A link to the same place as myString
var sameString = myString;
// If I change sameString, it will not modify myString,
// it just re-assigns it to a whole new string
sameString = ''New string'';
console.log(myString); // logs ''Test string 1'';
console.log(sameString); // logs ''New string'';
Si tuviera que pasar myString como parámetro a una función, se comporta como si simplemente lo asignara a una nueva variable. Ahora, hagamos lo mismo, pero con una función en lugar de una tarea simple
function myFunc(sameString) {
// Re assignment.. again, it will not modify myString
sameString = ''New string'';
}
var myString = ''Test string 1'';
// This behaves the same as if we said sameString = myString
myFunc(myString);
console.log(myString); // Again, logs ''Test string 1'';
La única razón por la que puede modificar objetos cuando los pasa a una función es porque no está reasignando ... En cambio, los objetos pueden cambiarse o mutarse ... Una vez más, funciona de la misma manera.
var myObject = { name: ''Joe''; }
// Assignment - We simply link to the same object
var sameObject = myObject;
// This time, we can mutate it. So a change to myObject affects sameObject and visa versa
myObject.name = ''Jack'';
console.log(sameObject.name); // Logs ''Jack''
sameObject.name = ''Jill'';
console.log(myObject.name); // Logs ''Jill''
// If we re-assign it, the link is lost
sameObject = { name: ''Howard'' };
console.log(myObject.name); // Logs ''Jill''
Si tuviera que pasar myObject como parámetro a una función, se comporta como si simplemente lo asignara a una nueva variable. De nuevo, lo mismo con el mismo comportamiento exacto pero con una función.
function myFunc(sameObject) {
// We mutate the object, so the myObject gets the change too... just like before.
sameObject.name = ''Jill'';
// But, if we re-assign it, the link is lost
sameObject = { name: ''Howard'' };
}
var myObject = { name: ''Joe''; }
// This behaves the same as if we said sameObject = myObject;
myFunc(myObject);
console.log(myObject.name); // Logs ''Jill''
Cada vez que pasa una variable a una función, está "Asignando" a cualquiera que sea el nombre del parámetro, como si usara el signo igual (=).
Recuerde siempre que el signo igual (=) significa asignación. Y pasar un parámetro a una función también significa asignación. Son iguales y las 2 variables están conectadas exactamente de la misma manera.
La única vez que la modificación de una variable afecta a una variable diferente es cuando se muta el objeto subyacente.
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 solo usara el signo igual para asignarlo a una nueva variable.
Piensa en esto, de esta manera:
Cada vez que cree un objeto en ECMAscript, este objeto se formará en un lugar universal místico de ECMAscript donde ningún hombre podrá obtener. Todo lo que recibes es una referencia a ese objeto en este lugar místico.
var obj = { };
Incluso obj
es solo una referencia al objeto (que se encuentra en ese lugar maravilloso especial) y, por lo tanto, solo puede pasar esta referencia . Efectivamente, cualquier fragmento de código que acceda a obj modificará el objeto que está lejos, muy lejos.
Sin purismos, creo que la mejor manera de emular un argumento escalar por referencia en Javascript es usar un objeto, como dice la respuesta anterior.
Sin embargo, lo hago un poco diferente:
He realizado la asignación de objetos dentro de la llamada de función, por lo que uno puede ver los parámetros de referencia cerca de la llamada de función. Aumenta la legibilidad de la fuente.
En la declaración de funciones, puse las propiedades como un comentario, por la misma razón: legibilidad.
var r;
funcWithRefScalars(r = {amount:200, message:null} );
console.log(r.amount + " - " + r.message);
function funcWithRefScalars(o) { // o(amount, message)
o.amount *= 1.2;
o.message = "20% increase";
}
En el ejemplo anterior, null
indica claramente un parámetro de referencia de salida.
La salida:
240 - 20% Increase
En el lado del cliente, console.log
debe ser reemplazado por alert
.
★ ★ ★
Otro método que puede ser aún más legible:
var amount, message;
funcWithRefScalars(amount = [200], message = [null] );
console.log(amount[0] + " - " + message[0]);
function funcWithRefScalars(amount, message) { // o(amount, message)
amount[0] *= 1.2;
message[0] = "20% increase";
}
Aquí ni siquiera necesitas crear nuevos nombres ficticios, como r
arriba.