384 votes

Passer des variables par référence en JavaScript

Comment passer des variables par référence en JavaScript ?

J'ai trois variables sur lesquelles je veux effectuer plusieurs opérations. Je veux donc les placer dans une boucle for et effectuer les opérations sur chacune d'entre elles.

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 variables

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

36 votes

Vous parlez de "passage par référence", mais vous n'avez pas d'appel de fonction dans votre exemple, donc il n'y a pas de passage du tout dans votre exemple. Veuillez préciser ce que vous essayez de faire.

1 votes

Désolé pour la confusion. Je n'avais pas spécifiquement besoin d'écrire une fonction, donc "passer par référence" était un mauvais choix de mots. Je veux juste pouvoir effectuer certaines opérations sur des variables sans avoir à écrire makePretty(var1); makePretty(var2); makePretty(var3); ...

1 votes

Sur la base de votre commentaire : arr = [var1, var2, var3]; for (var i = 0, len = arr.length; i < len; i++) { arr[i] = makePretty(arr[i]); } -- il suffit de stocker la valeur retournée par makePretty dans l'emplacement prévu à cet effet dans le tableau.

527voto

Pointy Points 172438

Il n'y a pas de "pass by reference" en JavaScript. Vous pouvez passer un objet (c'est-à-dire une référence à un objet) et demander à une fonction de modifier le contenu de l'objet :

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

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

alterObject(myObj);

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

Vous pouvez itérer sur les propriétés d'un tableau avec un index numérique et modifier chaque cellule du tableau, si vous le souhaitez.

var arr = [1, 2, 3];

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

Il est important de noter que le terme "pass-by-reference" est très spécifique. Il 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 simple variable de manière à permettre à une fonction de modifier cette valeur dans l'élément appel contexte. C'est ainsi que.. :

 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 comme le C++, il est possible de le faire parce que ce langage fait ont (en quelque sorte) la possibilité de passer par référence.

éditer - Cette affaire a récemment (mars 2015) explosé sur Reddit à propos d'un article de blog similaire au mien mentionné ci-dessous, bien que dans ce cas à propos de Java. Il m'est apparu, en lisant les allers-retours dans les commentaires de Reddit, qu'une grande partie de la confusion provient de la collision malheureuse impliquant le mot "référence". La terminologie "pass by reference" et "pass by value" est antérieure au concept d'"objets" dans les langages de programmation. Il ne s'agit pas du tout d'objets, mais de paramètres de fonctions, et plus précisément de la manière dont les paramètres de fonctions sont "connectés" (ou non) à l'environnement d'appel. En particulier, il convient de noter que dans un véritable langage de passage par référence - un langage qui fait impliquer des objets - on aurait toujours la possibilité de modifier l'objet contenu et cela ressemblerait à peu près à ce qui se passe en JavaScript. Cependant, on pourrait également de modifier la référence de l'objet dans l'environnement de l'appelant, et c'est la chose essentielle que vous devez faire. ne peut en JavaScript. Un langage "pass-by-reference" ne transmettrait pas la référence elle-même, mais un référence à la référence .

éditer - Voici un billet de blog sur le sujet. (Notez le commentaire à cet article qui explique que le C++ n'a pas vraiment de pass-by-reference. C'est vrai. Ce que le C++ a, cependant, c'est la possibilité de créer des références à des variables simples, soit explicitement au moment de l'invocation d'une fonction pour créer un pointeur, soit implicitement lors de l'appel de fonctions dont la signature du type d'argument le prévoit. Ce sont les principales choses que JavaScript ne prend pas en charge).

12 votes

Vous pouvez passer une référence à un objet ou à un tableau, ce qui vous permet de modifier l'objet ou le tableau d'origine, ce qui, je crois, est la question posée par l'OP.

2 votes

L'OP a utilisé cette terminologie, mais le code réel ne semble pas impliquer de "passage" :-) Je ne sais pas vraiment ce qu'il essaie de faire.

0 votes

C'est la bonne réponse, mais ce n'est pas ce que j'avais l'intention de demander. Je souhaite simplement effectuer des opérations sur une pléthore de variables sans écrire : makePretty(a); makePretty(b); makePretty(c); ...etc... Des solutions ?

174voto

Mukund Kumar Points 736
  1. Les variables de type primitif, comme les chaînes de caractères et les nombres, sont toujours transmises par leur valeur.
  2. Les tableaux et les objets sont transmis 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 by Value.

     object1 = { prop: "car" };
     array1 = [1,2,3];
  • si vous modifiez la valeur d'une propriété d'un objet ou d'un tableau, il s'agit alors d'un passage 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 { prop: "laptop" }
console.log(object2); // output: Object { prop: "bike" }
console.log(number1); // ouput: 10

37 votes

Ce n'est pas le sens de l'expression "passer par référence". Ce terme n'a rien à voir avec les objets, mais avec la relation entre les paramètres d'une fonction appelée et les variables de l'environnement de l'appelant.

19 votes

Tout est toujours transmis par sa valeur. Avec les non-primitives, la valeur transmise est une référence. L'affectation à une référence modifie cette référence - naturellement, puisqu'il s'agit d'une valeur - et ne peut donc pas modifier l'autre objet qui était référencé à l'origine. La modification de l'objet pointé par une référence agit exactement comme vous vous y attendez, en modifiant l'objet pointé. Les deux scénarios sont parfaitement cohérents, et aucun ne modifie magiquement le fonctionnement de JavaScript en remontant dans le temps et en changeant la façon dont ils ont été transmis à la fonction.

18 votes

Cette réponse clarifie la précédente, qui, même si elle a plus de votes (287 au moment où j'écris ces lignes et qu'elle est aussi celle qui a été acceptée) n'était pas assez claire sur la façon de la passer par référence en JS. En résumé, le code de cette réponse a fonctionné, mais pas la réponse qui a recueilli le plus de voix.

31voto

user2410595 Points 121

Solution pour passer une variable comme une référence :

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

inc('a');

alert(a); // 2

Et oui, vous pouvez le faire sans accéder à une variable globale :

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

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

    return init;
})();

inc();

0 votes

@Phil c'est bien de faire attention aux valeurs globales/fenêtres, mais à un moment donné tout ce que nous faisons dans le navigateur est un enfant ou un descendant de l'objet fenêtre. Dans nodejs, tout est un descendant de GLOBAL. Dans les langages à objets compilés, il s'agit d'un objet parent implicite, sinon explicite, parce que le fait de procéder autrement rend la gestion du tas plus compliquée (et pour quoi faire ?).

1 votes

@dkloke : Oui, l'objet fenêtre doit éventuellement être touché - comme jQuery utilise window.$/window.jQuery et d'autres méthodes en font partie. Je parlais de la pollution de l'espace de noms global où l'on ajoute beaucoup de variables plutôt que de les avoir sous un espace de noms unificateur.

7 votes

Je ne trouve pas de mots justes pour exprimer à quel point cette approche est mauvaise ;(

19voto

Eduardo Cuomo Points 1433

Objet simple

function foo(x) {
  // Function with other context
  // Modify `x` property, increasing the value
  x.value++;
}

// Initialize `ref` as object
var ref = {
  // The `value` is inside `ref` variable object
  // The initial value is `1`
  value: 1
};

// Call function with object value
foo(ref);
// Call function with object value again
foo(ref);

console.log(ref.value); // Prints "3"

Objet personnalisé

Objet rvar

/**
 * Aux function to create by-references variables
 */
function rvar(name, value, context) {
  // If `this` is a `rvar` instance
  if (this instanceof rvar) {
    // Inside `rvar` context...

    // Internal object value
    this.value = value;

    // Object `name` property
    Object.defineProperty(this, 'name', { value: name });

    // Object `hasValue` property
    Object.defineProperty(this, 'hasValue', {
      get: function () {
        // If the internal object value is not `undefined`
        return this.value !== undefined;
      }
    });

    // Copy value constructor for type-check
    if ((value !== undefined) && (value !== null)) {
      this.constructor = value.constructor;
    }

    // To String method
    this.toString = function () {
      // Convert the internal value to string
      return this.value + '';
    };
  } else {
    // Outside `rvar` context...

    // Initialice `rvar` object
    if (!rvar.refs) {
      rvar.refs = {};
    }

    // Initialize context if it is not defined
    if (!context) {
      context = this;
    }

    // Store variable
    rvar.refs[name] = new rvar(name, value, context);

    // Define variable at context
    Object.defineProperty(context, name, {
      // Getter
      get: function () { return rvar.refs[name]; },
      // Setter
      set: function (v) { rvar.refs[name].value = v; },
      // Can be overrided?
      configurable: true
    });

    // Return object reference
    return context[name];
  }
}

// Variable Declaration

// Declare `test_ref` variable
rvar('test_ref_1');

// Assign value `5`
test_ref_1 = 5;
// Or
test_ref_1.value = 5;

// Or declare and initialize with `5`:
rvar('test_ref_2', 5);

// ------------------------------
// Test Code

// Test Function
function Fn1(v) { v.value = 100; }

// Test
function test(fn) { console.log(fn.toString()); console.info(fn()); }

// Declare
rvar('test_ref_number');

// First assign
test_ref_number = 5;
test(() => test_ref_number.value === 5);

// Call function with reference
Fn1(test_ref_number);
test(() => test_ref_number.value === 100);

// Increase value
test_ref_number++;
test(() => test_ref_number.value === 101);

// Update value
test_ref_number = test_ref_number - 10;
test(() => test_ref_number.value === 91);

0 votes

Votre exemple semble reposer sur des hypothèses que vous n'énoncez pas. Lorsque je l'exécute dans un JavaScript j'obtiens une erreur : /workspace/Main.js:43 context = window; ReferenceError: window is not defined

1 votes

@Ant_222 Code corrigé.

1 votes

Merci pour la correction. Maintenant ça marche, mais je me méfie de voter pour votre réponse parce que professionnel. JavaScript les programmeurs que j'ai consultés considèrent votre solution une abomination . Je me rends compte moi-même qu'elle est assez dangereuse dans la mesure où elle rend les variables non conformes à ce qu'elles semblent être. Néanmoins, votre rvar Cette solution a sans doute une valeur éducative.

4voto

Adam Wise Points 61

J'ai joué avec la syntaxe pour faire ce genre de choses, mais cela nécessite quelques aides qui sont un peu inhabituelles. Pour commencer, on n'utilise pas du tout "var", mais une simple aide "DECLARE" qui crée une variable locale et définit sa portée via un rappel anonyme. En contrôlant la façon dont les variables sont déclarées, nous pouvons choisir de les envelopper dans des objets afin qu'elles puissent toujours être transmises par référence. Ceci est similaire à l'une des réponses d'Eduardo Cuomo ci-dessus, mais la solution ci-dessous ne nécessite pas l'utilisation de chaînes de caractères comme identifiants de variables. Voici un code minimal pour illustrer 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);
});

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X