web-dev-qa-db-fra.com

Passer les variables par référence en Javascript

Comment passer des variables par référence dans JS? J'ai 3 variables sur lesquelles je veux effectuer plusieurs opérations, donc je veux les mettre dans une boucle for et effectuer les opérations sur chacune d'entre elles.

pseudo code:

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

Quelle est la meilleure façon de procéder?

243
BFTrick

Il n'y a pas de "référence par référence" disponible en JavaScript. Vous pouvez passer un objet (c'est-à-dire, vous pouvez passer une référence à un objet, valeur par valeur), puis faire modifier le contenu de l'objet par une fonction:

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

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

alterObject(myObj);

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

Maintenant, dans votre cas, vous ne faites rien passer de toute façon, pour autant que je sache. Vous pouvez parcourir les propriétés d'un tableau avec un index numérique et modifier chaque cellule du tableau, si vous le souhaitez.

Il est important de noter que «référence par référence» est un terme très spécifique. Cela ne signifie pas simplement qu'il est possible de passer une référence à un objet modifiable. Cela signifie plutôt qu'il est possible de passer une variable simple de manière à permettre à une fonction de modifier cette valeur dans le contexte calling. Alors:

 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"

Dans un langage tel que C++, il est possible de le faire car ce langage fait (sort-of) a passe-à-référence.

(edit} _ - cela a récemment éclaté (mars 2015) sur Reddit pour un billet similaire à celui mentionné ci-dessous, mais dans le cas présent de Java. En lisant les allers-retours dans les commentaires de Reddit, je me suis dit qu'une grande partie de la confusion découlait de la collision malheureuse entre le mot "référence". La terminologie "passer par référence" et "passer par valeur" est antérieure à la notion de "objets" avec laquelle travailler dans les langages de programmation. Il ne s'agit vraiment pas d'objets du tout. il s'agit de paramètres de fonction, et plus précisément de la "connexion" (ou non) des paramètres de fonction à l'environnement appelant. En particulier, notez que dans un vrai langage de référence, qui implique ne implique des objets, on aurait toujours la possibilité de modifier l’objet contenu, et cela aurait plutôt l'air exactement comme en JavaScript. Cependant, on pourrait aussi pouvoir modifier la référence de l'objet dans l'environnement appelant, et c'est l'élément clé que vous ne pouvez pas faire en JavaScript. Un langage de référence ne transmettrait pas la référence elle-même, mais un référence à la référence.

edit - voici un article de blog sur le sujet. (Notez le commentaire de cet article qui explique que C++ n'a pas vraiment de référence de passage. C'est vrai. C++ a toutefois la possibilité de créer des références à des variables simples, soit explicitement au point d’appel de fonction pour créer un pointeur, ou implicitement lorsque vous appelez des fonctions dont la signature de type argument appelle cette opération. C’est là l’essentiel que JavaScript ne prend pas en charge.)

356
Pointy
  1. les variables de type primitif telles que les chaînes et les nombres sont toujours passées par valeur.
  2. Les tableaux et les objets sont passés par référence ou par valeur en fonction de ces conditions:

    • si vous définissez la valeur d'un objet ou d'un tableau, il s'agit de Pass par valeur.

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

    • si vous modifiez une valeur de propriété d'un objet ou d'un tableau, il s'agit de Pass par référence.

      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

85
Mukund Kumar

Solution de contournement pour passer variable comme par référence:

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

inc('a');

alert(a); // 2


MODIFIER

yup, en fait, vous pouvez le faire sans accès global

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

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

    return init;
})();

inc();
22
user2410595

Objet simple

var ref = { value: 1 };

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

Foo(ref);
Foo(ref);

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

Objet personnalisé

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

Déclaration de variable

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

Ou:

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

Code de test

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

Résultat de la console de test

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

Une autre approche permettant de passer toutes les variables (locales, primitives) par référence consiste à envelopper les variables avec la fermeture "à la volée" de eval. Cela fonctionne aussi avec "use strict". (Remarque: sachez que eval n'est pas convivial pour les optimiseurs JS. Des guillemets manquants autour du nom de la variable peuvent également entraîner des résultats imprévisibles.)

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

})()

Exemple live https://jsfiddle.net/t3k4403w/

5
Pavlo Mur

Il y a en fait une jolie solution:

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
Mateus Araújo

J'ai joué avec la syntaxe pour faire ce genre de chose, mais cela nécessite des assistants un peu inhabituels. Cela commence par ne pas utiliser du tout 'var', mais par un simple assistant 'DECLARE' qui crée une variable locale et en définit la portée via un rappel anonyme. En contrôlant la manière dont les variables sont déclarées, nous pouvons choisir de les envelopper dans des objets afin qu’elles puissent toujours être passées par référence, essentiellement. Ceci est similaire à l'une des réponses d'Eduardo Cuomo ci-dessus, mais la solution ci-dessous n'exige pas l'utilisation de chaînes comme identificateurs de variable. Voici un code minimal pour montrer le concept.

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
Adam Wise

Personnellement, je n'aime pas la fonctionnalité "passer par référence" offerte par divers langages de programmation. C'est peut-être parce que je découvre juste les concepts de la programmation fonctionnelle, mais j'ai toujours la chair de poule quand je vois des fonctions qui causent des effets secondaires (comme la manipulation de paramètres passés par référence). Personnellement, j'adhère fermement au principe de "responsabilité unique".

IMHO, une fonction devrait renvoyer un seul résultat/valeur en utilisant le mot-clé return. Au lieu de modifier un paramètre/argument, je voudrais simplement renvoyer la valeur modifiée du paramètre/argument et laisser toutes les réaffectations souhaitées jusqu'au code appelant.

Mais parfois (très rarement, espérons-le), il est nécessaire de renvoyer deux valeurs de résultat ou plus de la même fonction. Dans ce cas, je choisirais d'inclure toutes les valeurs résultantes dans une structure ou un objet unique. Encore une fois, le traitement de toute réaffectation devrait correspondre au code de l'appelant.

Exemple:

Supposons que les paramètres de transmission soient pris en charge en utilisant un mot clé spécial tel que 'ref' dans la liste des arguments. Mon code pourrait ressembler à quelque chose comme ça:

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

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

Au lieu de cela, je préférerais réellement faire quelque chose comme ceci:

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

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

Lorsque j'aurais besoin d'écrire une fonction qui renvoie plusieurs valeurs, je n'utiliserais pas non plus les paramètres passés par référence. Donc, je voudrais éviter un code comme celui-ci:

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

Au lieu de cela, je préférerais réellement renvoyer les deux nouvelles valeurs à l'intérieur d'un objet, comme ceci:

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

Ces exemples de code sont assez simplifiés, mais ils montrent approximativement comment je gérerais personnellement ce genre de choses. Cela m'aide à garder diverses responsabilités au bon endroit.

Bonne codage. :)

1
Bart Hofland

Mis à part la discussion sur la référence, les personnes qui recherchent toujours une solution à la question posée pourraient utiliser:

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

Je sais exactement ce que vous voulez dire. Même chose à Swift ne posera pas de problème… .. L’essentiel est d'utiliser let pas var.

Le fait que les primitives soient passées par valeur mais que la valeur de var i au moment de l'itération ne soit pas copiée dans la fonction anonyme est pour le moins surprenant.

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

JS n'étant pas du type fort, il vous permet de résoudre les problèmes de différentes manières, comme cela semble être le cas dans cette bande de roulement.

Cependant, du point de vue de la maintenabilité, je suis d’accord avec Bart Hofland. Function devrait faire en sorte que les arguments fassent quelque chose et renvoient le résultat. Ce qui les rend facilement réutilisables. 

Si vous estimez que les variables doivent être transmises par référence, vous serez peut-être mieux servi. Construisez-les dans des objets IMHO.

0
Michel S

Javascript peut modifier les éléments d'un tableau à l'intérieur d'une fonction (elle est transmise comme référence à l'objet/au tableau).

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

Voici un autre exemple:

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]
0