Przekazywanie zmiennych przez odniesienie w Javascript

Jak przekazać zmienne przez odniesienie w JS? Mam 3 zmienne, do których chcę wykonać kilka operacji, więc chcę umieścić je w pętli for i wykonać operacje dla każdej z nich.

Pseudo kod:

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

Jaki jest najlepszy sposób, aby to zrobić?

Author: Ian Goldby, 2011-10-12

10 answers

W JavaScript nie ma "pass by reference". Można przekazać obiekt (co oznacza, że można przekazać-by-value odniesienie do obiektu), a następnie mieć funkcję modyfikować zawartość obiektu:

function alterObject(obj) {
  obj.foo = "goodbye";
}

var myObj = { foo: "hello world" };

alterObject(myObj);

alert(myObj.foo); // "goodbye" instead of "hello world"
W Twoim przypadku i tak nic nie przekazujesz, o ile mi wiadomo. Możesz iterować właściwości tablicy z indeksem numerycznym i zmodyfikować każdą komórkę tablicy, jeśli chcesz.

Należy pamiętać, że "pass-by-reference" jest bardzo specyficznym termin. Nie oznacza to po prostu, że możliwe jest przekazanie odniesienia do modyfikowalnego obiektu. Zamiast tego oznacza to, że można przekazać prostą zmienną w taki sposób, aby funkcja mogła zmodyfikować tę wartość w kontekście wywołania . Więc:

 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"

W języku takim jak C++, jest to możliwe, ponieważ ten język ma (sort-of) pass-by-reference.

edit - to niedawno (marzec 2015) wybuchł na Reddit ponownie przez blog post podobny do mojej wymienionej poniżej, choć w tym przypadku o Javie. Podczas czytania komentarzy na Reddicie przyszło mi do głowy, że duża część zamieszania wynika z niefortunnej kolizji z udziałem słowa "reference". Terminologia "pass by reference "I" pass by value "poprzedzała koncepcję posiadania "obiektów" do pracy z językami programowania. Tak naprawdę nie chodzi o obiekty, chodzi o parametry funkcji, a w szczególności o to, jak parametry funkcji są "połączone" (lub nie) do środowiska wywołującego. W szczególności należy zauważyć, że w prawdziwym języku pass-by-reference-takim, w którym nie zawiera obiektów-nadal można by modyfikować zawartość obiektu , A wyglądałoby to dokładnie tak, jak w JavaScript. Jednakże, można również być w stanie zmodyfikować odniesienie do obiektu w środowisku wywołującym, a to jest kluczowa rzecz, której nie można zrobić w JavaScript. Pass-by-reference language by pass nie samo odniesienie, ale odniesienie do odniesienia.

edytuj - oto wpis na blogu na ten temat. (zwróć uwagę na komentarz do tego postu, który wyjaśnia, że C++ tak naprawdę nie ma pass-by-reference. To prawda. C++ ma jednak możliwość tworzenia odwołań do zwykłych zmiennych, albo jawnie w punkcie wywołania funkcji w celu utworzenia wskaźnika, albo domyślnie podczas wywoływania funkcji, których sygnatura typu argumentu trzeba to zrobić. Są to kluczowe rzeczy, których JavaScript nie obsługuje.)

 338
Author: Pointy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-12 18:11:53
  1. zmienne typu prymitywnego, takie jak ciągi znaków i liczby, są zawsze przekazywane przez wartość.
  2. Tablice i obiekty są przekazywane przez odniesienie lub przez wartość na podstawie tych warunków:

    • Jeśli ustawiasz wartość obiektu lub tablicy, jest ona przekazywana przez wartość.

      object1 = {prop: "car"}; array1 = [1,2,3];

    • Jeśli zmieniasz wartość właściwości obiektu lub tablicy, to jest ona przekazywana Referencja.

      object1.prop = "car"; array1[0] = 9;

Kod

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
 73
Author: Mukund Kumar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-19 14:52:45

Obejście przekazania zmiennej jak przez odniesienie:

var a = 1;
inc = function(variableName) {
  window[variableName] += 1;
};

inc('a');

alert(a); // 2


EDIT

Yup, faktycznie można to zrobić bez dostępu globalnego

inc = (function () {
    var variableName = 0;

    var init = function () {
        variableName += 1;
        alert(variableName);
    }

    return init;
})();

inc();
 22
Author: user2410595,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-03-18 04:23:27

Prosty Obiekt

var ref = { value: 1 };

function Foo(x) {
    x.value++;
}

Foo(ref);
Foo(ref);

alert(ref.value); // Alert: 3

Obiekt Niestandardowy

Obiekt 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];
    }
}

Deklaracja Zmiennej

rvar('test_ref');
test_ref = 5; // test_ref.value = 5

Lub:

rvar('test_ref', 5); // test_ref.value = 5

Kod Badania

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');

Wynik Testu Konsoli

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 
 10
Author: Eduardo Cuomo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-12-09 11:49:14

Innym podejściem do przekazywania dowolnych (lokalnych, prymitywnych) zmiennych przez odniesienie jest zawijanie zmiennej z zamknięciem" w locie " przez eval. Działa to również z "use strict". (Uwaga: Należy pamiętać, że {[1] } nie jest przyjazny dla optymalizatorów JS, również brak cudzysłowów wokół nazwy zmiennej może powodować nieprzewidywalne wyniki)

"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);

})()

Próbka na żywo https://jsfiddle.net/t3k4403w/

 4
Author: Pavlo Mur,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-16 09:52:06

Bawiłem się składnią, aby robić tego rodzaju rzeczy, ale wymaga to kilku pomocników, które są trochę nietypowe. Zaczyna się od tego, że w ogóle nie używa się 'var', ale prostego' DECLARE ' helpera, który tworzy zmienną lokalną i definiuje dla niej Zakres za pomocą anonimowego wywołania zwrotnego. Kontrolując sposób deklarowania zmiennych, możemy zawinąć je w obiekty tak, aby zawsze mogły być przekazywane przez odniesienie, zasadniczo. Jest to podobne do jednej z powyższych odpowiedzi Eduardo Cuomo, ale poniższe rozwiązanie nie wymaga używania ciągów znaków jako identyfikatorów zmiennych. Oto jakiś minimalny kod, aby pokazać koncepcję.

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);
});
 2
Author: Adam Wise,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-05-14 18:47:03

Jest w sumie całkiem ładny sollution:

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"]
 2
Author: Mateus Araújo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-09 16:56:00

To naprawdę proste.]}

Problem polega na zrozumieniu, że po przekazaniu klasycznych argumentów, jesteś ograniczony do innej, strefy tylko do odczytu.

Rozwiązanie polega na przekazywaniu argumentów za pomocą obiektowego projektu JavaScript,

Jest to to samo, co umieszczenie args w zmiennej globalnej / zakresowej, ale lepsze...

function action(){
  /* process this.arg, modification allowed */
}

action.arg = [ ["empty-array"],"some string",0x100,"last argument" ];
action();

Możesz również obiecać rzeczy, aby cieszyć się znanym łańcuchem: oto całość, z promise-like struktura

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")()
Albo jeszcze lepiej.. action.setArg(["empty-array"],"some string",0x100,"last argument").call()
 1
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-04 21:36:32

Osobiście nie lubię funkcji "pass by reference" oferowanych przez różne języki programowania. Być może dlatego, że dopiero odkrywam koncepcje programowania funkcyjnego, ale zawsze dostaję gęsiej skórki, gdy widzę funkcje, które powodują skutki uboczne (jak manipulowanie parametrami przekazywanymi przez odniesienie). Osobiście mocno popieram zasadę "jednej odpowiedzialności".

IMHO, funkcja powinna zwracać tylko jeden wynik / wartość używając słowa kluczowego return. Zamiast modyfikować parametr / argument, chciałbym po prostu zwrócić zmodyfikowaną wartość parametru / argumentu i pozostawić wszelkie pożądane zmiany do kodu wywołującego.

Ale czasami (miejmy nadzieję, że bardzo rzadko), konieczne jest zwrócenie dwóch lub więcej wartości wyniku z tej samej funkcji. W takim przypadku chciałbym włączyć wszystkie te wartości wynikowe w jedną strukturę lub obiekt. Ponownie, przetwarzanie wszelkich przypisań powinno być zgodne z kodem wywołującym.

Przykład:

Załóżmy, że przekazywanie parametrów być obsługiwane przez użycie specjalnego słowa kluczowego, takiego jak " ref " na liście argumentów. Mój kod może wyglądać mniej więcej tak:

//The Function
function doSomething(ref value) {
    value = "Bar";
}

//The Calling Code
var value = "Foo";
doSomething(value);
console.log(value); //Bar

Zamiast tego wolałbym zrobić coś takiego:

//The Function
function doSomething(value) {
    value = "Bar";
    return value;
}

//The Calling Code:
var value = "Foo";
value = doSomething(value); //Reassignment
console.log(value); //Bar

Kiedy muszę napisać funkcję zwracającą wiele wartości, nie używam też parametrów przekazywanych przez referencję. Więc unikałbym takiego kodu:

//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

Zamiast tego wolałbym zwracać obie nowe wartości wewnątrz obiektu, jak to:

//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);

Te przykłady kodu są dość uproszczone, ale z grubsza pokazuje, jak osobiście radziłbym sobie z takimi rzeczami. Pomaga mi to utrzymać różne obowiązki we właściwym miejscu.

Szczęśliwego kodowania. :)
 1
Author: Bart Hofland,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-22 19:50:38

Wiem dokładnie, co masz na myśli. To samo w Swifcie nie będzie problemem. Najważniejsze jest użycie let, a nie var.

Fakt, że prymitywy są przekazywane przez wartość, ale fakt, że wartość var i w punkcie iteracji nie jest kopiowana do funkcji anonimowej, jest co najmniej zaskakujące.

for (let i = 0; i < boxArray.length; i++) {
  boxArray[i].onclick = function() { console.log(i) }; // correctly prints the index
}
 0
Author: funct7,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-23 00:52:15