valor - Pasar variables por referencia en Javascript
variable por valor javascript (11)
¿Cómo paso las variables por referencia en JS? Tengo 3 variables a las que quiero realizar varias operaciones, así que quiero ponerlas en un bucle for y realizar las operaciones en cada una.
pseudo codigo
myArray = new Array(var1, var2, var3);
for (var x = 0; x < myArray.length; x++){
//do stuff to the array
makePretty(myArray[x]);
}
//now do stuff to the updated vars
¿Cuál es la mejor manera de hacer esto?
Objeto simple
var ref = { value: 1 };
function Foo(x) {
x.value++;
}
Foo(ref);
Foo(ref);
alert(ref.value); // Alert: 3
Objeto personalizado
Objeto rvar
function rvar (name, value, context) {
if (this instanceof rvar) {
this.value = value;
Object.defineProperty(this, ''name'', { value: name });
Object.defineProperty(this, ''hasValue'', { get: function () { return this.value !== undefined; } });
if ((value !== undefined) && (value !== null))
this.constructor = value.constructor;
this.toString = function () { return this.value + ''''; };
} else {
if (!rvar.refs)
rvar.refs = {};
if (!context)
context = window;
// Private
rvar.refs[name] = new rvar(name, value);
// Public
Object.defineProperty(context, name, {
get: function () { return rvar.refs[name]; },
set: function (v) { rvar.refs[name].value = v; },
configurable: true
});
return context[name];
}
}
Declaración Variable
rvar(''test_ref'');
test_ref = 5; // test_ref.value = 5
O:
rvar(''test_ref'', 5); // test_ref.value = 5
Código de prueba
rvar(''test_ref_number'');
test_ref_number = 5;
function Fn1 (v) { v.value = 100; }
console.log("rvar(''test_ref_number'');");
console.log("test_ref_number = 5;");
console.log("function Fn1 (v) { v.value = 100; }");
console.log(''test_ref_number.value === 5'', test_ref_number.value === 5);
console.log(" ");
Fn1(test_ref_number);
console.log("Fn1(test_ref_number);");
console.log(''test_ref_number.value === 100'', test_ref_number.value === 100);
console.log(" ");
test_ref_number++;
console.log("test_ref_number++;");
console.log(''test_ref_number.value === 101'', test_ref_number.value === 101);
console.log(" ");
test_ref_number = test_ref_number - 10;
console.log("test_ref_number = test_ref_number - 10;");
console.log(''test_ref_number.value === 91'', test_ref_number.value === 91);
console.log(" ");
console.log("---------");
console.log(" ");
rvar(''test_ref_str'', ''a'');
console.log("rvar(''test_ref_str'', ''a'');");
console.log(''test_ref_str.value === "a"'', test_ref_str.value === ''a'');
console.log(" ");
test_ref_str += ''bc'';
console.log("test_ref_str += ''bc'';");
console.log(''test_ref_str.value === "abc"'', test_ref_str.value === ''abc'');
Resultado de la consola de prueba
rvar(''test_ref_number'');
test_ref_number = 5;
function Fn1 (v) { v.value = 100; }
test_ref_number.value === 5 true
Fn1(test_ref_number);
test_ref_number.value === 100 true
test_ref_number++;
test_ref_number.value === 101 true
test_ref_number = test_ref_number - 10;
test_ref_number.value === 91 true
---------
rvar(''test_ref_str'', ''a'');
test_ref_str.value === "a" true
test_ref_str += ''bc'';
test_ref_str.value === "abc" true
en realidad es muy fácil,
el problema es comprender que una vez que se pasan los argumentos clásicos, estás dentro de otra zona de solo lectura.
soluciones es pasar los argumentos utilizando el diseño orientado a objetos de JavaScript,
es lo mismo que poner los argumentos en una variable global / con ámbito, pero mejor ...
function action(){
/* process this.arg, modification allowed */
}
action.arg = [ ["empty-array"],"some string",0x100,"last argument" ];
action();
También puedes prometer cosas para disfrutar de la conocida cadena: aquí está todo, con una estructura de promesa.
function action(){
/* process this.arg, modification allowed */
this.arg = ["a","b"];
}
action.setArg = function(){this.arg = arguments; return this;}
action.setArg(["empty-array"],"some string",0x100,"last argument")()
o mejor aún ... action.setArg(["empty-array"],"some string",0x100,"last argument").call()
- Las variables de tipo primitivo, como las cadenas y los números, siempre se pasan por valor.
Las matrices y los objetos se pasan por referencia o por valor en función de estas condiciones:
Si está configurando el valor de un objeto o matriz, es Pasar por valor.
object1 = {prop: "car"}; array1 = [1,2,3];
Si está cambiando el valor de una propiedad de un objeto o una matriz, entonces es Pasar por referencia.
object1.prop = "car"; array1[0] = 9;
Código
function passVar(obj1, obj2, num) {
obj1.prop = "laptop"; // will CHANGE original
obj2 = { prop: "computer" }; //will NOT affect original
num = num + 1; // will NOT affect original
}
var object1 = {
prop: "car"
};
var object2 = {
prop: "bike"
};
var number1 = 10;
passVar(object1, object2, number1);
console.log(object1); //output: Object {item:"laptop"}
console.log(object2); //output: Object {item:"bike"}
console.log(number1); //ouput: 10
En realidad hay una bonita solución:
function updateArray(context, targetName, callback) {
context[targetName] = context[targetName].map(callback);
}
var myArray = [''a'', ''b'', ''c''];
updateArray(this, ''myArray'', item => {return ''_'' + item});
console.log(myArray); //(3) ["_a", "_b", "_c"]
He estado jugando con la sintaxis para hacer este tipo de cosas, pero requiere algunos ayudantes que son un poco inusuales. Comienza sin usar ''var'' en absoluto, sino un simple ayudante ''DECLARE'' que crea una variable local y define un alcance para ella a través de una devolución de llamada anónima. Al controlar cómo se declaran las variables, podemos elegir envolverlas en objetos para que siempre puedan pasarse por referencia, esencialmente. Esto es similar a una de las respuestas anteriores de Eduardo Cuomo, pero la solución a continuación no requiere el uso de cadenas como identificadores de variables. Aquí hay un código mínimo para mostrar el concepto.
function Wrapper(val){
this.VAL = val;
}
Wrapper.prototype.toString = function(){
return this.VAL.toString();
}
function DECLARE(val, callback){
var valWrapped = new Wrapper(val);
callback(valWrapped);
}
function INC(ref){
if(ref && ref.hasOwnProperty(''VAL'')){
ref.VAL++;
}
else{
ref++;//or maybe throw here instead?
}
return ref;
}
DECLARE(5, function(five){ //consider this line the same as ''let five = 5''
console.log("five is now " + five);
INC(five); // increment
console.log("five is incremented to " + five);
});
Javascript puede modificar elementos de la matriz dentro de una función (se pasa como una referencia al objeto / matriz).
function makeAllPretty(items) {
for (var x = 0; x < myArray.length; x++){
//do stuff to the array
items[x] = makePretty(items[x]);
}
}
myArray = new Array(var1, var2, var3);
makeAllPretty(myArray);
Aquí hay otro ejemplo:
function inc(items) {
for (let i=0; i < items.length; i++) {
items[i]++;
}
}
let values = [1,2,3];
inc(values);
console.log(values);
// prints [2,3,4]
No hay "paso por referencia" disponible en JavaScript. Puede pasar un objeto (es decir, puede pasar una referencia a un objeto por valor) y luego hacer que una función modifique el contenido del objeto:
function alterObject(obj) {
obj.foo = "goodbye";
}
var myObj = { foo: "hello world" };
alterObject(myObj);
alert(myObj.foo); // "goodbye" instead of "hello world"
Ahora, en tu caso, no estás pasando nada de todos modos, por lo que puedo decir. Puede iterar sobre las propiedades de una matriz con un índice numérico y modificar cada celda de la matriz, si lo desea.
Es importante tener en cuenta que "paso por referencia" es un término muy específico. No significa simplemente que es posible pasar una referencia a un objeto modificable. En su lugar, significa que es posible pasar una variable simple de tal manera que permita a una función modificar ese valor en el contexto de la llamada . Asi que:
function swap(a, b) {
var tmp = a;
a = b;
b = tmp; //assign tmp to b
}
var x = 1, y = 2;
swap(x, y);
alert("x is " + x + " y is " + y); // "x is 1 y is 2"
En un lenguaje como C ++, es posible hacerlo porque ese lenguaje tiene (tipo de) paso por referencia.
Editar : esto recientemente (marzo de 2015) estalló en Reddit nuevamente en una publicación de blog similar a la mía que se menciona a continuación, aunque en este caso sobre Java. Se me ocurrió mientras leía los comentarios de ida y vuelta en los comentarios de Reddit que gran parte de la confusión se debe a la desafortunada colisión que involucra la palabra "referencia". La terminología "pasar por referencia" y "pasar por valor" es anterior al concepto de tener "objetos" para trabajar en lenguajes de programación. Realmente no se trata de objetos en absoluto; se trata de parámetros de función, y específicamente de cómo los parámetros de función están "conectados" (o no) al entorno de llamada. En particular, tenga en cuenta que en un verdadero lenguaje de paso por referencia (uno que involucre objetos), uno todavía tendría la capacidad de modificar el contenido de los objetos, y se vería casi exactamente como lo hace en JavaScript. Sin embargo, uno también podría modificar la referencia del objeto en el entorno de llamada, y eso es lo clave que no puede hacer en JavaScript. Un lenguaje de paso por referencia no pasaría la referencia en sí, sino una referencia a la referencia .
editar - aquí hay una entrada de blog sobre el tema. (Tenga en cuenta el comentario a esa publicación que explica que C ++ realmente no tiene paso por referencia. Eso es cierto. Sin embargo, lo que C ++ tiene es la capacidad de crear referencias a variables simples, ya sea explícitamente en el punto de función invocación para crear un puntero, o implícitamente al llamar a funciones cuyo tipo de argumento firma requiere que se haga eso. Esas son las cosas clave que JavaScript no admite).
Otro enfoque más para pasar cualquier variable (local, primitiva) por referencia es envolver la variable con el cierre "sobre la marcha" por eval
. Esto también funciona con "uso estricto". (Nota: tenga en cuenta que eval
no es amigable para los optimizadores JS, también las comillas que faltan en el nombre de la variable pueden causar resultados impredecibles)
"use strict"
//return text that will reference variable by name (by capturing that variable to closure)
function byRef(varName){
return "({get value(){return "+varName+";}, set value(v){"+varName+"=v;}})";
}
//demo
//assign argument by reference
function modifyArgument(argRef, multiplier){
argRef.value = argRef.value * multiplier;
}
(function(){
var x = 10;
alert("x before: " + x);
modifyArgument(eval(byRef("x")), 42);
alert("x after: " + x);
})()
Muestra en vivo https://jsfiddle.net/t3k4403w/
Personalmente no me gusta la funcionalidad "pasar por referencia" ofrecida por varios lenguajes de programación. Quizás sea porque estoy descubriendo los conceptos de la programación funcional, pero siempre me sale la piel de gallina cuando veo funciones que causan efectos secundarios (como la manipulación de parámetros que se pasan por referencia). Personalmente, adopto firmemente el principio de "responsabilidad única".
En mi humilde opinión, una función debería devolver solo un resultado / valor usando la palabra clave return. En lugar de modificar un parámetro / argumento, solo devolvería el valor del parámetro / argumento modificado y dejaría cualquier reasignación deseada al código de llamada.
Pero a veces (con suerte muy raramente), es necesario devolver dos o más valores de resultados de la misma función. En ese caso, optaría por incluir todos esos valores resultantes en una única estructura u objeto. Nuevamente, el procesamiento de cualquier reasignación debe estar a la altura del código de llamada.
Ejemplo:
Supongamos que los parámetros de paso se admitirían utilizando una palabra clave especial como ''ref'' en la lista de argumentos. Mi código podría ser algo como esto:
//The Function
function doSomething(ref value) {
value = "Bar";
}
//The Calling Code
var value = "Foo";
doSomething(value);
console.log(value); //Bar
En cambio, preferiría hacer algo como esto:
//The Function
function doSomething(value) {
value = "Bar";
return value;
}
//The Calling Code:
var value = "Foo";
value = doSomething(value); //Reassignment
console.log(value); //Bar
Cuando necesitaría escribir una función que devuelva múltiples valores, tampoco usaría parámetros pasados por referencia. Así evitaría un código como este:
//The Function
function doSomething(ref value) {
value = "Bar";
//Do other work
var otherValue = "Something else";
return otherValue;
}
//The Calling Code
var value = "Foo";
var otherValue = doSomething(value);
console.log(value); //Bar
console.log(otherValue); //Something else
En cambio, preferiría devolver ambos valores nuevos dentro de un objeto, como este:
//The Function
function doSomething(value) {
value = "Bar";
//Do more work
var otherValue = "Something else";
return {
value: value,
otherValue: otherValue
};
}
//The Calling Code:
var value = "Foo";
var result = doSomething(value);
value = result.value; //Reassignment
console.log(value); //Bar
console.log(result.otherValue);
Estos ejemplos de código están bastante simplificados, pero aproximadamente demuestran cómo yo personalmente manejaría tales cosas. Me ayuda a mantener varias responsabilidades en el lugar correcto.
Feliz codificacion :)
Sé exactamente a que te refieres. Lo mismo en Swift no será problema. La línea de fondo es el uso let
no var
.
El hecho de que las primitivas se pasen por valor, pero el hecho de que el valor de var i
en el punto de iteración no se copie en la función anónima es bastante sorprendente, como mínimo.
for (let i = 0; i < boxArray.length; i++) {
boxArray[i].onclick = function() { console.log(i) }; // correctly prints the index
}
Solución para pasar la variable como por referencia:
var a = 1;
inc = function(variableName) {
window[variableName] += 1;
};
inc(''a'');
alert(a); // 2
EDITAR
Sí, en realidad puedes hacerlo sin acceso global.
inc = (function () {
var variableName = 0;
var init = function () {
variableName += 1;
alert(variableName);
}
return init;
})();
inc();