Übergeben Sie Variablen als Referenz in Javascript

285

Wie übergebe ich Variablen als Referenz in JavaScript? Ich habe 3 Variablen, für die ich mehrere Operationen ausführen möchte, also möchte ich sie in eine for-Schleife einfügen und die Operationen für jede ausführen.

Pseudocode:

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

Was ist der beste Weg, dies zu tun?

BFTrick
quelle
31
Sie sprechen von "Referenzübergabe", haben jedoch in Ihrem Beispiel keine Funktionsaufrufe, sodass in Ihrem Beispiel überhaupt keine Übergabe erfolgt. Bitte klären Sie, was Sie versuchen zu tun.
jfriend00
1
Entschuldigung für die Verwirrung. Ich musste keine spezielle Funktion schreiben, daher war "Referenzübergabe" eine schlechte Wortwahl. Ich möchte nur in der Lage sein, einige Operationen an Variablen auszuführen, ohne zu schreiben makePretty(var1); makePretty(var2); makePretty(var3); ...
BFTrick
basierend auf Ihrem Kommentar: arr = [var1, var2, var3]; for (var i = 0, len = arr.length; i < len; i++) { arr[i] = makePretty(arr[i]); }- Sie müssen nur den von makePrettyback zurückgegebenen Wert in dem Slot im Array speichern .
dylnmc

Antworten:

415

In JavaScript ist keine "Referenzübergabe" verfügbar. Sie können ein Objekt übergeben (dh Sie können einen Verweis auf ein Objekt als Wert übergeben) und dann eine Funktion den Objektinhalt ändern lassen:

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

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

alterObject(myObj);

alert(myObj.foo); // "goodbye" instead of "hello world"

Sie können die Eigenschaften eines Arrays mit einem numerischen Index durchlaufen und bei Bedarf jede Zelle des Arrays ändern.

var arr = [1, 2, 3];

for (var i = 0; i < arr.length; i++) { 
    arr[i] = arr[i] + 1; 
}

Es ist wichtig zu beachten, dass "Pass-by-Reference" ein sehr spezifischer Begriff ist. Es bedeutet nicht einfach, dass es möglich ist, einen Verweis auf ein modifizierbares Objekt zu übergeben. Stattdessen bedeutet dies, dass es möglich ist, eine einfache Variable so zu übergeben, dass eine Funktion diesen Wert im aufrufenden Kontext ändern kann . So:

 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"

In einer Sprache wie C ++, ist es möglich , das zu tun , weil die Sprache tut (Art-of) hat Pass-by-reference.

edit - dies hat kürzlich (März 2015) Reddit erneut über einen Blog-Beitrag in die Luft gesprengt, der meinem unten erwähnten ähnelt, allerdings in diesem Fall über Java. Beim Lesen des Hin und Her in den Reddit-Kommentaren fiel mir ein, dass ein großer Teil der Verwirrung auf die unglückliche Kollision mit dem Wort "Referenz" zurückzuführen ist. Die Terminologie "Referenzübergabe" und "Wertübergabe" geht dem Konzept voraus, "Objekte" zu haben, mit denen in Programmiersprachen gearbeitet werden kann. Es geht wirklich überhaupt nicht um Objekte; Es geht um Funktionsparameter und insbesondere darum, wie Funktionsparameter mit der aufrufenden Umgebung "verbunden" sind (oder nicht). Bestimmtes,und es würde ziemlich genau so aussehen wie in JavaScript. Man könnte jedoch auch die Objektreferenz in der aufrufenden Umgebung ändern, und das ist der Schlüssel, den Sie in JavaScript nicht tun können. Eine Pass-by-Reference-Sprache würde nicht die Referenz selbst übergeben, sondern eine Referenz auf die Referenz .

bearbeiten - hier ist ein Blog-Beitrag zum Thema. (Beachten Sie den Kommentar zu diesem Beitrag, der erklärt, dass C ++ nicht wirklich über eine Referenz verfügt. Das stimmt. C ++ bietet jedoch die Möglichkeit, Verweise auf einfache Variablen zu erstellen, entweder explizit am Funktionspunkt Aufruf zum Erstellen eines Zeigers oder implizit beim Aufrufen von Funktionen, deren Argumenttypsignatur dies erfordert. Dies sind die wichtigsten Dinge, die JavaScript nicht unterstützt.)

Spitze
quelle
8
Sie können einen Verweis auf ein Objekt oder ein Array übergeben, mit dem Sie das ursprüngliche Objekt oder Array ändern können, nach dem das OP meiner Meinung nach tatsächlich fragt.
jfriend00
2
Nun, das OP hat die Terminologie verwendet, aber der eigentliche Code scheint überhaupt kein "Übergeben" zu beinhalten :-) Ich bin mir nicht sicher, was er versucht zu tun.
Pointy
5
Das Übergeben einer Referenz als Wert ist nicht dasselbe wie das Übergeben als Referenz , obwohl dies in einigen Szenarien wie diesem möglicherweise der Fall ist.
Travis Webb
5
Ihr Blogger ist falsch über C ++, das tut Pass-by-reference und sein Posten macht keinen Sinn. Es ist irrelevant, dass Sie den Wert einer Referenz nach der Initialisierung nicht ändern können. Das hat nichts mit "Pass by Reference" zu tun. Seine Aussage, dass die Semantik "Pass by Reference" durch C # zurückverfolgt werden kann, hätte eine Alarmglocke läuten müssen.
EML
7
@Pointy Dies ist eine schreckliche Antwort. Wenn ich Hilfe brauchte, wäre das Letzte, was ich möchte, jemand, der mich in Semantik schult. "Referenzübergabe" bedeutet einfach, "die Funktion kann bis zu einem gewissen Grad den Wert der Variablen ändern, die als Argument übergeben wurde." (im Kontext der Frage) Es ist wirklich nicht so kompliziert, wie Sie es sich vorstellen.
Crownlessking
108
  1. primitive Typvariablen wie Zeichenfolgen und Zahlen werden immer als Wert übergeben.
  2. Arrays und Objekte werden basierend auf diesen Bedingungen als Referenz oder als Wert übergeben:

    • Wenn Sie den Wert eines Objekts oder Arrays festlegen, ist dies Pass by Value.

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

    • Wenn Sie einen Eigenschaftswert eines Objekts oder Arrays ändern, handelt es sich um Pass by Reference.

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

Code

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

Mukund Kumar
quelle
21
Das ist nicht die Bedeutung von "Referenzübergabe". Der Begriff hat eigentlich nichts mit Objekten zu tun, sondern mit der Beziehung zwischen Parametern in einer aufgerufenen Funktion und Variablen in der aufrufenden Umgebung.
Pointy
12
Alles wird immer als Wert übergeben. Bei Nicht-Grundelementen ist der übergebene Wert eine Referenz. Durch das Zuweisen zu einer Referenz wird diese Referenz geändert - natürlich, da es sich um einen Wert handelt - und kann daher das andere Objekt, auf das ursprünglich verwiesen wurde, nicht ändern. Das Mutieren des Objekts, auf das durch eine Referenz verwiesen wird, funktioniert genau so, wie Sie es erwarten würden, indem das Objekt, auf das verwiesen wird, mutiert wird. Beide Szenarien sind vollkommen konsistent, und keines verändert auf magische Weise die Funktionsweise von JavaScript, indem es in die Vergangenheit zurückreicht und ändert, wie sie an die Funktion übergeben wurden.
Matthew Read
9
Diese Antwort verdeutlichte die vorherige, die, obwohl sie mehr Stimmen hat (287 zum Zeitpunkt des Schreibens und es ist auch die akzeptierte), nicht klar genug war, wie sie tatsächlich als Referenz in JS übergeben werden sollte. Kurz gesagt, der Code in dieser Antwort funktionierte, die Antwort, die mehr Stimmen erhielt, nicht.
Pap
25

Problemumgehung zum Übergeben von Variablen wie durch Referenz:

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

inc('a');

alert(a); // 2


BEARBEITEN

Ja, eigentlich können Sie es ohne globalen Zugriff tun

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

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

    return init;
})();

inc();
user2410595
quelle
@Phil Es ist gut, vorsichtig mit globalen / Fensterwerten zu sein, aber irgendwann ist alles, was wir im Browser tun, ein Kind oder ein Nachkomme des Fensterobjekts. In nodejs ist alles ein Nachkomme von GLOBAL. In kompilierten Objektsprachen ist dies ein implizites, wenn nicht explizites übergeordnetes Objekt, da dies sonst die Heap-Verwaltung komplizierter macht (und wofür?).
Dkloke
1
@dkloke: Ja, eventuell muss das Fensterobjekt berührt werden, so wie jQuery das Fenster verwendet. $ / window.jQuery und andere Methoden befinden sich darunter. Ich habe über die Verschmutzung des globalen Namespace gesprochen, bei dem Sie eine Menge Variablen hinzufügen, anstatt sie unter einem einheitlichen Namespace zu haben.
Phil
2
Ich kann keine guten Worte finden, um auszudrücken, wie schlecht dieser Ansatz ist; (
SOReader
Ich liebe die letzte Lösung (bearbeitete Version), auch wenn sie keine Variable als Referenz übergibt. Dies ist von Vorteil, da Sie direkt auf die Variablen zugreifen können.
John Boe
11

Einfaches Objekt

var ref = { value: 1 };

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

Foo(ref);
Foo(ref);

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

Benutzerdefiniertes Objekt

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

Variable Aussage

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

Oder:

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

Testcode

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

Ergebnis der Testkonsole

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 
Eduardo Cuomo
quelle
5

Ein weiterer Ansatz, um (lokale, primitive) Variablen als Referenz zu übergeben, besteht darin, die Variable mit dem Abschluss "on the fly" zu verpacken eval. Dies funktioniert auch mit "use strict". (Hinweis: Beachten Sie, dass dies evalnicht für JS-Optimierer geeignet ist. Auch fehlende Anführungszeichen um den Variablennamen können zu unvorhersehbaren Ergebnissen führen.)

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

})()

Live-Beispiel https://jsfiddle.net/t3k4403w/

Pavlo Mur
quelle
Das ist großartig
hard_working_ant
3

Es gibt eigentlich eine hübsche Lösung:

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"]
Mateus Araújo
quelle
2

Ich habe mit der Syntax herumgespielt, um so etwas zu tun, aber es erfordert einige Helfer, die etwas ungewöhnlich sind. Es beginnt damit, dass 'var' überhaupt nicht verwendet wird, sondern ein einfacher 'DECLARE'-Helfer, der eine lokale Variable erstellt und über einen anonymen Rückruf einen Bereich dafür definiert. Indem wir steuern, wie Variablen deklariert werden, können wir sie in Objekte einschließen, sodass sie im Wesentlichen immer als Referenz übergeben werden können. Dies ähnelt einer der obigen Antworten von Eduardo Cuomo, für die folgende Lösung müssen jedoch keine Zeichenfolgen als Variablenbezeichner verwendet werden. Hier ist ein minimaler Code, um das Konzept zu zeigen.

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);
});
Adam Wise
quelle
2

eigentlich ist es wirklich einfach,

Das Problem besteht darin, zu verstehen, dass Sie nach dem Übergeben klassischer Argumente in eine andere schreibgeschützte Zone versetzt werden.

Die Lösung besteht darin, die Argumente mit dem objektorientierten Design von JavaScript zu übergeben.

Es ist dasselbe wie das Einfügen der Argumente in eine globale Variable / Gültigkeitsbereichsvariable, aber besser ...

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

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

Sie können auch versprechen Sachen die bekannte Kette zu genießen bis: hier ist das Ganze, mit Versprechen artigen Struktur

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

oder noch besser.. action.setArg(["empty-array"],"some string",0x100,"last argument").call()


quelle
this.argfunktioniert nur mit actionInstanz. Das funktioniert nicht.
Eduardo Cuomo
2

Ich persönlich mag die "Pass by Reference" -Funktionalität, die von verschiedenen Programmiersprachen angeboten wird, nicht. Vielleicht liegt das daran, dass ich gerade die Konzepte der funktionalen Programmierung entdecke, aber ich bekomme immer Gänsehaut, wenn ich Funktionen sehe, die Nebenwirkungen verursachen (wie das Manipulieren von Parametern, die als Referenz übergeben werden). Ich persönlich begrüße nachdrücklich das Prinzip der "Einzelverantwortung".

IMHO sollte eine Funktion nur ein Ergebnis / einen Wert mit dem Schlüsselwort return zurückgeben. Anstatt einen Parameter / ein Argument zu ändern, würde ich einfach den geänderten Parameter / Argument-Wert zurückgeben und alle gewünschten Neuzuweisungen dem aufrufenden Code überlassen.

Aber manchmal (hoffentlich sehr selten) ist es notwendig, zwei oder mehr Ergebniswerte von derselben Funktion zurückzugeben. In diesem Fall würde ich mich dafür entscheiden, alle resultierenden Werte in eine einzelne Struktur oder ein einzelnes Objekt aufzunehmen. Auch hier sollte die Verarbeitung von Neuzuweisungen dem aufrufenden Code entsprechen.

Beispiel:

Angenommen, die Übergabe von Parametern wird durch die Verwendung eines speziellen Schlüsselworts wie 'ref' in der Argumentliste unterstützt. Mein Code könnte ungefähr so ​​aussehen:

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

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

Stattdessen würde ich eigentlich lieber so etwas machen:

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

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

Wenn ich eine Funktion schreiben müsste, die mehrere Werte zurückgibt, würde ich auch keine Parameter verwenden, die als Referenz übergeben werden. Also würde ich Code wie diesen vermeiden:

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

Stattdessen würde ich es vorziehen, beide neuen Werte innerhalb eines Objekts wie folgt zurückzugeben:

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

Diese Codebeispiele sind ziemlich vereinfacht, aber sie zeigen grob, wie ich persönlich mit solchen Dingen umgehen würde. Es hilft mir, verschiedene Verantwortlichkeiten am richtigen Ort zu halten.

Viel Spaß beim Codieren. :) :)

Bart Hofland
quelle
Das Zuweisen und Neuzuweisen von Dingen (auch ohne Typprüfung) führt zu Gänsehaut. Was die Verantwortung betrifft, erwarte ich, dass doSomething () das tut, nicht das Ding macht und ein Objekt plus macht und meine Variablen Eigenschaften zuweist. Angenommen, ich habe ein Array, das durchsucht werden muss. Ich möchte die passenden Elemente in einem zweiten Array platzieren und möchte wissen, wie viele gefunden wurden. Eine Standardlösung wäre, eine Funktion wie diese aufzurufen: 'var foundArray; if ((findStuff (inArray, & foundArray))> 1) {// foundArray verarbeiten} '. Nirgendwo in diesem Szenario ist ein neues, unbekanntes Objekt wünschenswert oder erforderlich.
Elise van Looij
@ElisevanLooij In Ihrem Beispielfall würde ich es vorziehen, findStuffeinfach das resultierende Array zurückzugeben. Sie müssen auch eine foundArrayVariable deklarieren , damit ich ihr das resultierende Array direkt zuweisen kann : var foundArray = findStuff(inArray); if (foundArray.length > 0) { /* process foundArray */ }. Dies würde 1) den aufrufenden Code lesbarer / verständlicher machen und 2) die interne Funktionalität (und damit auch die Testbarkeit) der findStuffMethode erheblich vereinfachen , wodurch sie in verschiedenen (Wieder-) Anwendungsfällen / Szenarien tatsächlich viel flexibler wird.
Bart Hofland
@ElisevanLooij Ich stimme jedoch zu, dass Neuzuweisungen (wie in meiner Antwort) tatsächlich ein "Codegeruch" sind, und ich möchte diese auch so weit wie möglich vermeiden. Ich werde darüber nachdenken, meine Antwort so zu bearbeiten (oder sogar neu zu formulieren), dass sie meine tatsächliche Meinung zu diesem Thema besser widerspiegelt. Danke für deine Reaktion. :)
Bart Hofland
1

Javascript kann Array-Elemente innerhalb einer Funktion ändern (es wird als Referenz auf das Objekt / Array übergeben).

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

Hier ist ein weiteres Beispiel:

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]
Michiel van der Blonk
quelle
1

Da JS kein starker Typ ist, können Sie Probleme auf viele verschiedene Arten lösen, wie in diesem Schritt dargestellt.

Aus Sicht der Wartbarkeit müsste ich jedoch Bart Hofland zustimmen. Die Funktion sollte Argumente dazu bringen, etwas zu tun, und das Ergebnis zurückgeben. Einfach wiederverwendbar machen.

Wenn Sie der Meinung sind, dass Variablen als Referenz übergeben werden müssen, ist es möglicherweise besser, sie in Objekte einzubauen, IMHO.

Michel S.
quelle
1

Abgesehen von der Pass-by-Reference-Diskussion könnten diejenigen, die noch nach einer Lösung für die genannte Frage suchen, Folgendes verwenden:

const myArray = new Array(var1, var2, var3);
myArray.forEach(var => var = makePretty(var));
Shira Joseph
quelle
0

Ich weiß genau was du meinst. Das Gleiche in Swift wird kein Problem sein. Unterm Strich wird letnicht verwendetvar .

Die Tatsache, dass Grundelemente als Wert übergeben werden, aber die Tatsache, dass der Wert var izum Zeitpunkt der Iteration nicht in die anonyme Funktion kopiert wird, ist gelinde gesagt ziemlich überraschend.

for (let i = 0; i < boxArray.length; i++) {
  boxArray[i].onclick = function() { console.log(i) }; // correctly prints the index
}
funct7
quelle