Wskaźniki w JavaScript?

81

Czy możemy przekazać referencję do zmiennej, która jest niezmienna jako argument w funkcji?

Przykład:

var x = 0;
function a(x)
{
    x++;
}
a(x);
alert(x); //Here I want to have 1 instead of 0
user1342369
źródło
Można argumentować, że jest to zły styl, ponieważ zamierzasz użyć skutków ubocznych funkcji, aby wywołać mutację. Może to utrudnić śledzenie kodu.
spex

Odpowiedzi:

140

Ponieważ JavaScript nie obsługuje przekazywania parametrów przez odniesienie, musisz zamiast tego uczynić zmienną obiektem:

var x = {Value: 0};

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

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

W tym przypadku xjest to odniesienie do obiektu. Kiedy xjest przekazywane do funkcji a, to odwołanie jest kopiowane do obj. Tak więc obji xodnoszą się do tego samego w pamięci. Zmiana Valuewłaściwości objwpływa na Valuewłaściwość x.

JavaScript zawsze przekazuje parametry funkcji według wartości. To po prostu specyfikacja języka. Państwo mogłoby stworzyć xw zakresie lokalnego do obu funkcji, a nie przekazać zmienną w ogóle.

Mike Christensen
źródło
Czy jest na to sposób, aby inna osoba po prostu to zrobiła: var x = 0; a (x); i że funkcja ustawiła obiekt o wartości x? (lub coś w tym stylu)
user1342369
6
Hej, dziękuję za wprowadzenie mnie do fragmentów kodu w SO . Nie słyszałem o nich, zanim zobaczyłem, jak zostało to użyte w twojej odpowiedzi.
Joeytje50
@ Joeytje50 - Tak, ta funkcja została właśnie dodana we wrześniu. Bardzo przydatne!
Mike Christensen
Co się stanie, jeśli nie znasz nazwy właściwości obj, na przykład jeśli była to obj.name, to następnym razem, gdy będziesz chciał zaktualizować obj.s nazwisko? Jak dostosować funkcję do tego rodzaju elastyczności?
Sir
1
@Dave to może być późna odpowiedź, ale możesz sprawić, że Twoja funkcja będzie oczekiwała na 3 parametry, obiekt, nazwę właściwości i wartość. Pamiętaj, że możesz uzyskać dostęp do właściwości obiektu, wykonując obj.propertyname lub obj ['propertyname'], co będzie sposobem, w jaki chcesz, jeśli chcesz elastyczności. Przekaż 3 parametry i do obj ['propertyname'] = wartość i będzie można ją ponownie wykorzystać.
Carlos Calla
19

To pytanie może pomóc: jak przekazać zmienną przez odniesienie w javascript? Odczytaj dane z funkcji ActiveX, która zwraca więcej niż jedną wartość

Podsumowując, typy pierwotne Javascript są zawsze przekazywane przez wartość, podczas gdy wartości wewnątrz obiektów są przekazywane przez referencje (dzięki komentatorom za zwrócenie uwagi). Aby to obejść, musisz umieścić swoją liczbę całkowitą wewnątrz obiektu:

var myobj = {x:0};

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

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

  

n00dle
źródło
2
Zmienne obiektowe są zawsze przekazywane przez wartość - po prostu wartość jest odniesieniem do obiektu. Istnieje różnica między przekazywaniem przez odwołanie a przekazywaniem odwołania przez wartość . Mianowicie, kiedy przekazujesz xwartość, niezależnie od tego, czy xjest to obiekt, czy nie, nie możesz zastąpić jej całą inną wartością w żaden sposób, który wywołujący zobaczy.
cHao
podczas gdy obiekty są przekazywane przez odniesienie, jest po prostu nieprawdą. Nic w JavaScript nie jest przekazywane przez odniesienie.
Mike Christensen
Jeśli przekazujesz typ naczelnych według wartości (odwołuje się do x), dlaczego x nie zmienia się później, chyba że przekazujesz obiekt
SuperUberDuper,
14

Znalazłem nieco inny sposób implementacji wskaźników, który jest być może bardziej ogólny i łatwiejszy do zrozumienia z perspektywy C (a zatem bardziej pasuje do formatu przykładu użytkownika).

W JavaScript, podobnie jak w C, zmienne tablicowe są w rzeczywistości tylko wskaźnikami do tablicy, więc możesz użyć tablicy tak samo, jak zadeklarowanie wskaźnika. W ten sposób wszystkie wskaźniki w kodzie mogą być używane w ten sam sposób, pomimo tego, jak nazwaliście zmienną w oryginalnym obiekcie.

Pozwala również na użycie dwóch różnych notacji odnoszących się do adresu wskaźnika i tego, co znajduje się pod adresem wskaźnika.

Oto przykład (podkreślenia używam do oznaczenia wskaźnika):

var _x = [ 10 ];

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

foo(_x);

console.log(_x[0]);

Plony

output: 20
William Oliver
źródło
8

Odwołujesz się do „x” z obiektu okna

var x = 0;

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

a('x');                   // string
alert(x);
AlexisK
źródło
3

Późna odpowiedź, ale natknąłem się na sposób przekazywania prymitywnych wartości przez odniesienie za pomocą domknięć. Utworzenie wskaźnika jest dość skomplikowane, ale działa.

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

W ES6 kod można skrócić do użycia wyrażeń lambda:

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

helloWorld(myNamePtr); // joe, jack
console.log(myName); // jack
Himanshu
źródło
1

W JavaScript byłoby to globalne. Jednak twoja funkcja wyglądałaby bardziej tak:

function a(){
   x++;
};

Ponieważ xjest w kontekście globalnym, nie musisz przekazywać go do funkcji.

Maess
źródło
1
Ważny punkt, chociaż chciałbym zwrócić uwagę, że zmienna nie musi być globalna (i wielu odradza robienie tego kiedykolwiek), po prostu musi znajdować się w tym samym zakresie, co wywołujący i wywoływany.
Mike Christensen
1

JavaScript powinien po prostu wstawiać wskaźniki do miksu, ponieważ rozwiązuje wiele problemów. Oznacza to, że kod może odnosić się do nieznanej nazwy zmiennej lub zmiennych, które zostały utworzone dynamicznie. Ułatwia również modułowe kodowanie i wstrzykiwanie.

Oto, co uważam za najbliższe wskazówki, jakie można osiągnąć w praktyce

w 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

w 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
źródło
1

Może to być niemożliwe, ponieważ JavaScript nie ma operatora „\” Perla, aby uzyskać prymityw przez odniesienie, ale istnieje sposób na utworzenie obiektu „efektywnego wskaźnika” dla prymitywu przy użyciu tego wzorca.

To rozwiązanie jest najbardziej sensowne, gdy masz już prymityw (więc nie możesz już umieścić go w obiekcie bez konieczności modyfikowania innego kodu), ale nadal musisz przekazać do niego wskaźnik, aby inne części kodu majstrowały ze swoim stanem; więc nadal możesz majstrować przy jego stanie za pomocą płynnego proxy, które zachowuje się jak wskaźnik.

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
źródło
0

W JavaScript nie można przekazywać zmiennych przez odwołanie do funkcji. Możesz jednak przekazać obiekt przez odniesienie.

d4rkpr1nc3
źródło
3
Po prostu nieprawda. Nie możesz przekazać niczego przez odwołanie w JavaScript.
Mike Christensen
1
Odwołanie do obiektu jest przekazywane jako wartość, więc wszystko jest przekazywane jako wartość.
Carlos Calla
ciekawa obserwacja; wydaje się NIEMOŻLIWE utworzenie odniesienia do istniejącego prymitywu w JavaScript; po związaniu prymitywu wszelkie zmiany tego prymitywu nigdy nie mogą zostać zauważone przez jego naiwne odnośniki, ponieważ odnośniki wskazują na wartość, którą jest zmienna, która okazała się być prymitywem; więc jedynym sposobem na „wskazanie” prymitywu jest umieszczenie go w zmiennej, której typeof to „Object”; ponieważ żadne inne typy nie mogą przechowywać odniesienia. TLDR; w JavaScript nie ma operatora „\” Perla; jest to albo odniesienie na początek, albo nigdy nie możesz go wskazać.
Dmitry
Irytujące jest to, gdy chcesz utworzyć zbiór wspólnych stanów, np. "Window.shared" jako mapę odniesień do zmiennych, które są obecnie puste / niezdefiniowane, które ustawisz później; w tym przypadku, gdy ustawisz wartości stanu, który jest aktualnie pusty / niezdefiniowany, zmienna "window.shared" nie rozpozna zmian, mimo że są one zapisane w typie Object, jest już za późno; Aby to zadziałało, wartości „null / undefined” muszą znajdować się wewnątrz typów Object, np. [null] [undefined].
Dmitry
To powiedziawszy, możesz być sprytny i umieścić funkcje w takiej zmiennej globalnej, która ma getter i setter dla każdej z tych jeszcze nie ustawionych zmiennych, a te funkcje dynamicznie znajdą stan CURRENT prymitywu i pobiorą / ustawią go, ale nie może zwrócić wskaźnika do tej zmiennej (ale może zwrócić proxy, które może efektywnie zachowywać się tak samo jak ta zmienna i modyfikować je pośrednio, używając metod pobierających / ustawiających).
Dmitry
0

Myślę, że w przeciwieństwie do C lub dowolnego języka ze wskazówkami:

/** Javascript **/
var o = {x:10,y:20};
var o2 = {z:50,w:200};
  • oczywiście w javascript nie można uzyskać dostępu do obiektów o i o2 adresów w pamięci
  • ale nie możesz też porównywać ich adresów: (brak trywialnej możliwości ich sortowania , a następnie dostęp przez dychotomię )

.

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

to ogromny problem:

  • oznacza to, że możesz wyświetlić / zarządzać wszystkimi obiektami utworzonymi (zaalokowanymi) przez Twoją aplikację,

  • z tej ogromnej listy obiektów obliczyć trochę informacji o nich (na przykład jak są ze sobą połączone)

  • ale kiedy chcesz pobrać informacje, które utworzyłeś o określonym obiekcie, nie możesz ich znaleźć w swoim ogromnej liście przez dychotomię: nie ma unikalnego identyfikatora dla obiektu, który mógłby zostać użyty jako zamiennik prawdziwego adresu pamięci

to w końcu oznacza, że ​​jest to ogromny problem, jeśli chcesz pisać w javascript:

  • JavaScript debugger / IDE
  • lub a specjalnie zoptymalizowany garbage collector
  • struktura danych szuflada / analizator
reuns
źródło
0

JavaScript nie obsługuje przekazywania typów pierwotnych przez odwołanie. Istnieje jednak obejście tego problemu.

Umieść wszystkie zmienne, które musisz przekazać w obiekcie. W tym przypadku istnieje tylko jedna zmienna x. Zamiast przekazywać zmienną, przekaż nazwę zmiennej jako ciąg, którym jest "x".

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

clickbait
źródło
0

W zależności od tego, co chcesz zrobić, możesz po prostu zapisać nazwę zmiennej, a następnie uzyskać do niej dostęp później:

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

var myFavoriteNumber = 6; 

toAccessMyVariable("myFavoriteNumber");

Aby zastosować się do konkretnego przykładu, możesz zrobić coś takiego:

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

function a(variableName)
{
    window[variableName]++;
}
a(pointerToX);
alert(x); //Here I want to have 1 instead of 0
Lwica
źródło
Działa to tylko w środowisku przeglądarki i jest specyficzne dla windowobiektu. Ponadto stosowanie zmiennych globalnych jest mniej zalecane, ponieważ może już istnieć zmienna o tej samej nazwie.
JayJay,
0

Po prostu nie obsługuje wskaźników, koniec historii :-(.

Chciałbym dodać, że chociaż jestem zupełnie nowy i pochodzę z tła C, z tego, co czytam o pętli zdarzeń i stosie, powinieneś trzymać dowolny obiekt jak najbliżej wywołującego. Ponieważ jestem nowy, mogłem się pomylić.

Wiele odpowiedzi sugeruje, aby uczynić go globalnym, ale ponownie, jeśli dobrze czytam, może to zwiększyć liczbę skoków stosu i w pewnym sensie niepotrzebnie pofragmentować pętlę zdarzeń (w zależności od tego, co aktualnie robi wywołany obiekt).

Ponadto każdy przykład, który naśladuje wskaźniki, jest tylko 1 na wiele sposobów, aby zrobić to samo, co nie będzie wskaźnikiem.


źródło
-1

W twoim przykładzie faktycznie masz 2 zmienne o tej samej nazwie. (Globalna) zmienna x i zakres funkcji x. Ciekawe, że javascript, gdy ma możliwość wyboru, co zrobić z 2 zmiennymi o tej samej nazwie, idzie z nazwą o zakresie funkcji i ignoruje zmienną spoza zakresu.

Prawdopodobnie nie jest bezpiecznie zakładać, że javascript zawsze będzie się zachowywał w ten sposób ...

Twoje zdrowie!

tpaytn
źródło
Ta odpowiedź nie dodaje żadnej wartości do tego posta. Odpowiedź @Mike Christensen jest bardziej szczegółowa i już zaakceptowana.
Alexander,
-1

Nie, nie możesz. Przeglądarka nie daje dostępu do pamięci systemowej i jej zmiennych. Powinno to być możliwe w środowisku takim jak węzeł, ale wewnątrz przeglądarki nie jest to całkiem możliwe, chyba że jesteś hakerem.

Ionut Eugen
źródło