web-dev-qa-db-fra.com

Passer une chaîne par référence en Javascript

Je souhaite créer une chaîne et la transmettre par référence de manière à pouvoir modifier une seule variable et la transmettre à tout autre objet qui la référence.

Prenons cet exemple:

function Report(a, b) {
    this.ShowMe = function() { alert(a + " of " + b); }
}

var metric = new String("count");
var a = new Report(metric, "a"); 
var b = new Report(metric, "b"); 
var c = new Report(metric, "c"); 
a.ShowMe();  // outputs:  "count of a";
b.ShowMe();  // outputs:  "count of b";
c.ShowMe();  // outputs:  "count of c";

Je veux pouvoir que cela se produise:

var metric = new String("count");
var a = new Report(metric, "a"); 
var b = new Report(metric, "b"); 
var c = new Report(metric, "c"); 
a.ShowMe();  // outputs:  "count of a";
metric = new String("avg");
b.ShowMe();  // outputs:  "avg of b";
c.ShowMe();  // outputs:  "avg of c";

Pourquoi ça ne marche pas?

La référence MDC sur les chaînes indique que la métrique est un objet.

J'ai essayé ceci, ce qui n'est pas ce que je veux, mais qui est très proche:

var metric = {toString:function(){ return "count";}};
var a = new Report(metric, "a"); 
var b = new Report(metric, "b"); 
var c = new Report(metric, "c"); 
a.ShowMe();  // outputs:  "count of a";
metric.toString = function(){ return "avg";}; // notice I had to change the function
b.ShowMe();  // outputs:  "avg of b";
c.ShowMe();  // outputs:  "avg of c";

alert(String(metric).charAt(1)); // notice I had to use the String constructor
// I want to be able to call this: 
// metric.charAt(1)

Les points importants ici:

  1. Je veux pouvoir utiliser metric comme s'il s'agissait d'un objet chaîne normal
  2. Je veux que chaque rapport fasse référence au même objet.
40
Jeff Meatball Yang

Les chaînes en Javascript sont déjà passées "par référence" - appeler une procédure avec une chaîne n'implique pas de copier le contenu de la chaîne. Il y a deux problèmes à résoudre:

  • Les cordes sont immuables. Contrairement aux chaînes C++, une fois qu'une chaîne JavaScript a été créée, elle ne peut plus être modifiée.
  • En JavaScript, les variables ne sont pas des emplacements attribués de manière statique comme en C++. metric est dans votre code une étiquette qui s'applique à deux variables de chaîne entièrement distinctes.

Voici un moyen d’atteindre ce que vous voulez, en utilisant des fermetures pour implémenter la portée dynamique de metric:

function Report(a, b) {
    this.ShowMe = function() { alert(a() + " of " + b); }
}

var metric = "count";
var metric_fnc = function() { return metric; }
var a = new Report(metric_fnc, "a"); 
var b = new Report(metric_fnc, "b"); 
a.ShowMe();  // outputs:  "count of a";
metric = "avg";
b.ShowMe();  // outputs:  "avg of b";
52
John Millikin

Vous pouvez envelopper la chaîne dans un objet et modifier le champ dans lequel la chaîne est stockée dans . Ceci est similaire à ce que vous faites dans le dernier exemple, sans qu'il soit nécessaire de modifier les fonctions.

var metric = { str : "count" } 
metric.str = "avg";

Maintenant, metric.str contiendra "avg"

31
Matthew Manela

Fermeture?

var metric = new function() {
    var _value = "count";

    this.setValue = function(s) { _value = s; };
    this.toString = function() { return _value; };
};

// snip ...
a.ShowMe();

metric.setValue("avg");
b.ShowMe();
c.ShowMe();

ou en le rendant un peu plus générique et performant:

function RefString(s) {
    this.value = s;
}

RefString.prototype.toString = function() { return this.value; }
RefString.prototype.charAt = String.prototype.charAt;

var metric = new RefString("count");

// snip ...

a.ShowMe();

metric.value = "avg";
b.ShowMe();
c.ShowMe();

Si vous ne fermez pas sur la variable de chaîne souhaitée, alors je suppose que la seule autre solution consisterait à modifier la fonction ShowMe, comme dans la réponse de John Millikin ou à restructurer la base de code.

10
chakrit

Si vous transmettez la variable en tant qu’objet, cela fonctionnera, car les objets sont passés par référence en Javascript. 

http://sirdarckcat.blogspot.com/2007/07/passing-reference-to-javascript.html

function modifyVar(obj,newVal){
obj.value=newVal;
}
var m={value: 1};
alert(x);
modifyVar("x",321);
alert(x);
3
george9170

Jsfiddle: https://jsfiddle.net/ncwhmty7/1/

Les primitives de chaîne (String Literal) et les objets String sont immuables. Cela signifie que les modifications apportées au contenu d'une variable de chaîne dans la fonction sont complètement séparées de tout ce qui se produit en dehors de la fonction. Il existe plusieurs options pour surmonter ceci: 

1. Retour de la valeur de la fonction modifiée à partir de la fonction 

function concatenateString(stringA, stringB) {
    return stringA + stringB;
}
alert(concatenateString("Hi", " there"));

2. Pour convertir la variable chaîne en un véritable objet

function modifyVar(stringA, stringB) {
    var result = stringA + stringB;
    stringA.valueOf = stringA.toSource = stringA.toString = function() {
        return result;
    };
}
var stringA = Object('HI');
modifyVar(stringA, ' there');
alert(stringA);
1
Razan Paul

Si cette chaîne est une propriété d'un objet, vous pouvez envoyer l'objet et la chaîne de caractères de la propriété:

    this.modificationFunction = function(object, stringKey){
      object[stringKey] = object[stringKey] + "bla bla bla";
    }

    myObject.myStringProperty = "She said: ";
    this.modificationFunction(myObject, "myStringProperty"); 
    // myObject.myStringProperty is now "She said: bla bla bla";
0
Marek Manduch

En JavaScript, les chaînes sont immuables. Vous ne pouvez pas modifier la chaîne elle-même si les instances Report ont un descripteur.

votre solution fonctionne, mais cela peut être plus simple:

function Report(a, b) {
  this.showMe = function() { alert(a.str + " of " + b); }
}

var metric = {};
metric.str = "count";

a.Showme();
metric.str = "avg";
b.ShowMe();
0
orip