¿Punteros en JavaScript?

81

¿Podemos pasar una referencia de una variable que sea inmutable como argumento en una función?

Ejemplo:

var x = 0;
function a(x)
{
    x++;
}
a(x);
alert(x); //Here I want to have 1 instead of 0
usuario1342369
fuente
Se puede argumentar que este es un mal estilo, ya que tiene la intención de utilizar los efectos secundarios de una función para causar una mutación. Esto puede hacer que su código sea más difícil de seguir.
Spex

Respuestas:

140

Dado que JavaScript no admite el paso de parámetros por referencia, deberá convertir la variable en un objeto:

var x = {Value: 0};

function a(obj)
{
    obj.Value++;
}

a(x);
document.write(x.Value); //Here i want to have 1 instead of 0

En este caso, xes una referencia a un objeto. Cuando xse pasa a la función a, esa referencia se copia a obj. Así, objy se xrefieren a lo mismo en la memoria. Cambiar la Valuepropiedad de objafecta la Valuepropiedad de x.

Javascript siempre pasará los parámetros de función por valor. Eso es simplemente una especificación del idioma. Usted podría crear xen un ámbito local para ambas funciones, y no pasar la variable en absoluto.

Mike Christensen
fuente
¿Hay alguna manera de hacerlo para que otra persona simplemente haga: var x = 0; a (x); y que la función establece un objeto con el valor de x? (o algo así)
user1342369
6
Hola, gracias por presentarme fragmentos de código en SO . No había oído hablar de ellos antes de verlo usado en su respuesta.
Joeytje50
@ Joeytje50 - Sí, esta función se acaba de agregar en septiembre. ¡Muy útil!
Mike Christensen
¿Qué sucede si no conoce el nombre de la propiedad obj, por ejemplo, si era obj.name, la próxima vez que desee actualizar obj.surname? ¿Cómo se puede hacer que una función se adapte a ese tipo de flexibilidad?
Sir
1
@Dave, esta puede ser una respuesta tardía, pero puede hacer que su función espere 3 parámetros, el obj, el nombre de la propiedad y el valor. Recuerde que puede acceder a una propiedad de un objeto haciendo obj.propertyname u obj ['propertyname'], que será la forma que desee cuando desee flexibilidad. Pase 3 parámetros y haga obj ['propertyname'] = value y eso será reutilizable.
Carlos Calla
19

Esta pregunta puede ayudar: ¿Cómo pasar variables por referencia en javascript? Leer datos de la función ActiveX que devuelve más de un valor

Para resumir, los tipos primitivos de Javascript siempre se pasan por valor, mientras que los valores dentro de los objetos se pasan por referencia (gracias a los comentaristas por señalar mi descuido). Entonces, para evitar esto, debes poner tu número entero dentro de un objeto:

var myobj = {x:0};

function a(obj)
{
    obj.x++;
}

a(myobj);
alert(myobj.x); // returns 1

  

n00dle
fuente
2
Las variables de objeto también se pasan siempre por valor; es solo que el valor es una referencia a un objeto. Hay una diferencia entre pasar por referencia y pasar una referencia por valor . Es decir, cuando pasa xpor valor, ya xsea ​​un objeto o no, no puede reemplazarlo con un valor completamente diferente de cualquier manera que vea la persona que llama.
cHao
mientras que los objetos se pasan por referencia simplemente no es cierto. Nada en JavaScript se pasa por referencia.
Mike Christensen
Si pasa un tipo de primate por valor (se hace referencia a él mediante x), ¿por qué x no cambia después, a menos que pase el objeto?
SuperUberDuper
14

He encontrado una forma ligeramente diferente de implementar punteros que es quizás más general y más fácil de entender desde una perspectiva de C (y por lo tanto encaja más en el formato del ejemplo de los usuarios).

En JavaScript, como en C, las variables de matriz son en realidad solo punteros a la matriz, por lo que puede usar una matriz exactamente igual que declarar un puntero. De esta manera, todos los punteros en su código se pueden usar de la misma manera, a pesar del nombre de la variable en el objeto original.

También permite usar dos notaciones diferentes que se refieren a la dirección del puntero y lo que está en la dirección del puntero.

Aquí hay un ejemplo (uso el guión bajo para indicar un puntero):

var _x = [ 10 ];

function foo(_a){
    _a[0] += 10;
}

foo(_x);

console.log(_x[0]);

Rendimientos

output: 20
William Oliver
fuente
8

Te refieres a 'x' del objeto de la ventana

var x = 0;

function a(key, ref) {
    ref = ref || window;  // object reference - default window
    ref[key]++;
}

a('x');                   // string
alert(x);
AlexisK
fuente
3

Respuesta tardía, pero me he encontrado con una forma de pasar valores primitivos por referencia mediante cierres. Es bastante complicado crear un puntero, pero funciona.

function ptr(get, set) {
    return { get: get, set: set };
}

function helloWorld(namePtr) {
    console.log(namePtr.get());
    namePtr.set('jack');
    console.log(namePtr.get())
}

var myName = 'joe';
var myNamePtr = ptr(
    function () { return myName; },
    function (value) { myName = value; }
);

helloWorld(myNamePtr); // joe, jack
console.log(myName); // jack

En ES6, el código se puede acortar para usar expresiones lambda:

var myName = 'joe';
var myNamePtr = ptr(=> myName, v => myName = v);

helloWorld(myNamePtr); // joe, jack
console.log(myName); // jack
Himanshu
fuente
1

En JavaScript, eso sería global. Sin embargo, su función se parecería más a esto:

function a(){
   x++;
};

Como xestá en el contexto global, no es necesario pasarlo a la función.

Maess
fuente
1
Punto válido, aunque señalaría que la variable no necesita ser global (y muchos desaconsejarían hacer esto nunca), simplemente debe estar en el mismo ámbito que la persona que llama y la persona que llama.
Mike Christensen
1

Javascript debería poner punteros en la mezcla porque resuelve muchos problemas. Significa que el código puede hacer referencia a un nombre de variable desconocido o variables que se crearon dinámicamente. También facilita la codificación modular y la inyección.

Esto es lo que veo como lo más cerca que puede llegar a los punteros c en la práctica

en js:

var a = 78;       // creates a var with integer value of 78 
var pointer = 'a' // note it is a string representation of the var name
eval (pointer + ' = 12'); // equivalent to: eval ('a = 12'); but changes value of a to 12

C ª:

int a = 78;       // creates a var with integer value of 78 
int pointer = &a; // makes pointer  to refer to the same address mem as a
*pointer = 12;   // changes the value of a to 12
Emmanuel Mahuni
fuente
1

Puede ser imposible ya que JavaScript no tiene el operador "\" de Perl para obtener una primitiva por referencia, pero hay una manera de crear un objeto "efectivamente un puntero" para una primitiva usando este patrón.

Esta solución tiene más sentido para cuando ya tiene el primitivo (por lo que ya no puede ponerlo en un objeto sin necesidad de modificar otro código), pero aún necesita pasarle un puntero para que otras partes de su código lo modifiquen. con su estado; por lo que aún puede jugar con su estado utilizando el proxy transparente que se comporta como un puntero.

var proxyContainer = {};

// | attaches a pointer-lookalike getter/setter pair
// | to the container object.
var connect = function(container) {
    // | primitive, can't create a reference to it anymore
    var cant_touch_this = 1337;

    // | we know where to bind our accessor/mutator
    // | so we can bind the pair to effectively behave
    // | like a pointer in most common use cases.
    Object.defineProperty(container, 'cant_touch_this', {
        'get': function() {
            return cant_touch_this;
        },                
        'set': function(val) {
            cant_touch_this = val;
        }
    });
};

// | not quite direct, but still "touchable"
var f = function(x) {
    x.cant_touch_this += 1;
};

connect(proxyContainer);

// | looks like we're just getting cant_touch_this
// | but we're actually calling the accessor.
console.log(proxyContainer.cant_touch_this);

// | looks like we're touching cant_touch_this
// | but we're actually calling the mutator.
proxyContainer.cant_touch_this = 90;

// | looks like we touched cant_touch_this
// | but we actually called a mutator which touched it for us.
console.log(proxyContainer.cant_touch_this);

f(proxyContainer);

// | we kinda did it! :)
console.log(proxyContainer.cant_touch_this);
Dmitry
fuente
0

En JavaScript, no puede pasar variables por referencia a una función. Sin embargo, puede pasar un objeto por referencia.

d4rkpr1nc3
fuente
3
Simplemente no es cierto. No puede pasar nada por referencia en JavaScript.
Mike Christensen
1
La referencia del objeto se pasa como valor, por lo que todo se pasa como valor.
Carlos Calla
observación interesante; parece IMPOSIBLE crear una referencia a una primitiva existente en JavaScript; una vez que un primitivo está ligado, cualquier cambio en este primitivo nunca puede ser notado por sus referidos ingenuos porque los referidos apuntan al valor de la variable, que resulta ser un primitivo; así que la única forma de "apuntar" a una primitiva es si la primitiva está envuelta en una variable cuyo tipo es "Objeto"; ya que ningún otro tipo puede almacenar una referencia. TLDR; no hay un operador Perl "\" en JavaScript; para empezar, es una referencia o nunca se puede señalar.
Dmitry
La parte molesta de esto es cuando desea crear una bolsa de estado compartido, por ejemplo, "window.shared" como un mapa de referencias a variables que actualmente son nulas / indefinidas y que establecerá más adelante; en este caso, al establecer los valores del estado que actualmente es nulo / indefinido, la variable "window.shared" no reconocerá los cambios, aunque estén almacenados en un tipo de Objeto, ya es demasiado tarde; los valores "nulo / indefinido" tienen que estar dentro de los tipos de objeto para que esto funcione, por ejemplo, [nulo] [indefinido].
Dmitry
Dicho esto, puede ser inteligente y poner funciones en una variable global de este tipo, que tiene un getter y setter para cada una de estas variables aún no establecidas, y estas funciones encontrarán dinámicamente el estado ACTUAL de la primitiva y la obtendrán / establecerán, pero no puede devolver un puntero a esa variable (pero puede devolver un proxy que puede comportarse efectivamente de la misma manera que esa variable y mutarla indirectamente, usando getters / setters).
Dmitry
0

Estoy pensando que, al contrario de C o cualquier idioma con punteros:

/** Javascript **/
var o = {x:10,y:20};
var o2 = {z:50,w:200};
  • obviamente en Javascript que no pueden acceder a los objetos o y o2 direcciones en la memoria
  • pero tampoco puedes comparar su dirección: (no hay posibilidad trivial de ordenarlos , y luego acceder por dicotomía )

.

o == o2 // obviously false : not the same address in memory
o <= o2 // true !
o >= o2 // also true !!

eso es un gran problema:

  • significa que puede enumerar / administrar todos los objetos creados (asignados) por su aplicación,

  • a partir de esa enorme lista de objetos, calcule alguna información sobre estos (cómo están vinculados, por ejemplo)

  • pero cuando desea recuperar la información que creó sobre un objeto específico, no puede encontrarla en su enorme lista por dicotomía: no hay un identificador único por objeto que pueda usarse como reemplazo de la dirección de memoria real

esto finalmente significa que este es un gran problema, si desea escribir en javascript:

  • un depurador javascript / IDE
  • o un recolector de basura especialmente optimizado
  • un cajón / analizador de estructura de datos
reencuentros
fuente
0

JavaScript no admite el paso de tipos primitivos por referencia. Sin embargo, hay una solución.

Pon todas las variables que necesitas para pasar en un objeto. En este caso, sólo hay una variable x. En lugar de pasar la variable, pase el nombre de la variable como una cadena, que es "x".

var variables = {x:0};
function a(x)
{
    variables[x]++;
}
a("x");
alert(variables.x);

clickbait
fuente
0

Dependiendo de lo que le gustaría hacer, simplemente puede guardar el nombre de la variable y luego acceder a él más adelante así:

function toAccessMyVariable(variableName){
  alert(window[variableName]);
}

var myFavoriteNumber = 6; 

toAccessMyVariable("myFavoriteNumber");

Para aplicar a su ejemplo específico, podría hacer algo como esto:

var x = 0;
var pointerToX = "x";

function a(variableName)
{
    window[variableName]++;
}
a(pointerToX);
alert(x); //Here I want to have 1 instead of 0
Leona
fuente
Esto solo funciona en un entorno de navegador y es específico del windowobjeto. Además, es menos recomendable utilizar variables globales, ya que podría haber una variable con el mismo nombre.
JayJay
0

Simplemente no admite punteros, fin de la historia :-(.

Me gustaría agregar que, aunque soy nuevo y provengo de un entorno C, por lo que estoy leyendo sobre el bucle de eventos y la pila, debe mantener el objeto lo más cerca posible de su llamador. Como soy nuevo, podría equivocarme.

Muchas respuestas aquí sugieren que sea global, pero nuevamente, si estoy leyendo correctamente, eso posiblemente podría aumentar los saltos de pila y, de alguna manera, posiblemente fragmentar su bucle de eventos innecesariamente (dependiendo de lo que el objeto llamado esté haciendo actualmente).

Además, cualquier ejemplo dado que imita punteros, es solo 1 en muchas formas para hacer lo mismo, que no será un puntero.


fuente
-1

En su ejemplo, en realidad tiene 2 variables con el mismo nombre. La variable (global) x y la función dentro del ámbito variable de x. Es interesante ver que javascript, cuando se le da la opción de qué hacer con 2 variables del mismo nombre, va con el nombre del ámbito de la función e ignora la variable fuera del ámbito.

Probablemente no sea seguro suponer que javascript siempre se comportará de esta manera ...

¡Salud!

tpaytn
fuente
Esta respuesta no agrega ningún valor a esta publicación. La respuesta de @Mike Christensen es más detallada y ya está aceptada.
Alexander
-1

No, no puedes. El navegador no le da acceso a la memoria del sistema y sus variables. Debería ser posible con un entorno como un nodo, pero dentro del navegador no es posible a menos que sea un hacker.

Ionut Eugen
fuente