Question Variables statiques en JavaScript


Comment puis-je créer des variables statiques en Javascript?


608
2017-10-08 04:31


origine


Réponses:


Si vous venez d'un langage orienté objet basé sur des classes et statiquement typé (comme Java, C ++ ou C #) Je suppose que vous essayez de créer une variable ou une méthode associée à un "type" mais pas à une instance.

Un exemple utilisant une approche "classique", avec des fonctions de constructeur peut-être pourrait vous aider à attraper les concepts de JavaScript OO de base:

function MyClass () { // constructor function
  var privateVariable = "foo";  // Private variable 

  this.publicVariable = "bar";  // Public variable 

  this.privilegedMethod = function () {  // Public Method
    alert(privateVariable);
  };
}

// Instance method will be available to all instances but only load once in memory 
MyClass.prototype.publicMethod = function () {    
  alert(this.publicVariable);
};

// Static variable shared by all instances
MyClass.staticProperty = "baz";

var myInstance = new MyClass();

staticProperty est défini dans l'objet MyClass (qui est une fonction) et n'a rien à voir avec ses instances créées, JavaScript traite les fonctions comme objets de première classe, étant ainsi un objet, vous pouvez assigner des propriétés à une fonction.


790
2017-10-08 04:49



Vous pourriez profiter du fait que les fonctions JS sont aussi des objets - ce qui signifie qu'elles peuvent avoir des propriétés.

Par exemple, en citant l'exemple donné sur l'article (maintenant disparu) Variables statiques en Javascript:

function countMyself() {
    // Check to see if the counter has been initialized
    if ( typeof countMyself.counter == 'undefined' ) {
        // It has not... perform the initialization
        countMyself.counter = 0;
    }

    // Do something stupid to indicate the value
    alert(++countMyself.counter);
}

Si vous appelez cette fonction plusieurs fois, vous verrez que le compteur est incrémenté.

Et c'est probablement une bien meilleure solution que de poluer l'espace de noms global avec une variable globale.


Et voici une autre solution possible, basée sur une fermeture: Astuce pour utiliser des variables statiques en javascript :

var uniqueID = (function() {
   var id = 0; // This is the private persistent value
   // The outer function returns a nested function that has access
   // to the persistent value.  It is this nested function we're storing
   // in the variable uniqueID above.
   return function() { return id++; };  // Return and increment
})(); // Invoke the outer function after defining it.

Ce qui vous donne le même type de résultat - sauf que, cette fois, la valeur incrémentée est retournée, au lieu d'être affichée.


492
2017-10-08 04:37



Vous le faites via un IIFE (expression de fonction invoquée immédiatement):

var incr = (function () {
    var i = 1;

    return function () {
        return i++;
    }
})();

incr(); // returns 1
incr(); // returns 2

76
2018-03-30 10:02



vous pouvez utiliser arguments.callee pour stocker des variables "statiques" (ceci est également utile dans les fonctions anonymes):

function () {
  arguments.callee.myStaticVar = arguments.callee.myStaticVar || 1;
  arguments.callee.myStaticVar++;
  alert(arguments.callee.myStaticVar);
}

38
2017-11-06 11:19



function Person(){
  if(Person.count == undefined){
    Person.count = 1;
  }
  else{
    Person.count ++;
  }
  console.log(Person.count);
}

var p1 = new Person();
var p2 = new Person();
var p3 = new Person();

26
2018-02-28 20:08



J'ai vu quelques réponses similaires, mais je voudrais mentionner que ce post le décrit le mieux, alors j'aimerais le partager avec vous.

Voici quelques exemples de code que j'ai modifié pour obtenir un exemple complet qui, espérons-le, donne des avantages à la communauté car il peut être utilisé comme modèle de conception pour les classes.

Ça aussi répond à votre question:

function Podcast() {

    // private variables
    var _somePrivateVariable = 123;

    // object properties (read/write)
    this.title = 'Astronomy Cast';
    this.description = 'A fact-based journey through the galaxy.';
    this.link = 'http://www.astronomycast.com';

    // for read access to _somePrivateVariable via immutableProp 
    this.immutableProp = function() {
        return _somePrivateVariable;
    }

    // object function
    this.toString = function() {
       return 'Title: ' + this.title;
    }
};

// static property
Podcast.FILE_EXTENSION = 'mp3';
// static function
Podcast.download = function(podcast) {
    console.log('Downloading ' + podcast + ' ...');
};

Étant donné cet exemple, vous pouvez accéder à propriétés statiques / fonction comme suit:

// access static properties/functions
Podcast.FILE_EXTENSION;                // 'mp3'
Podcast.download('Astronomy cast');    // 'Downloading Astronomy cast ...'

Et le propriétés / fonctions de l'objet simplement comme:

// access object properties/functions
var podcast = new Podcast();
podcast.title = 'The Simpsons';
console.log(podcast.toString());       // Title: The Simpsons
console.log(podcast.immutableProp());  // 123

Remarque que dans podcast.immutableProp (), nous avons un fermeture: La référence à _somePrivateVariable est conservée dans la fonction.

Vous pouvez même définir getters et setters. Jetez un oeil à cet extrait de code (où d est le prototype de l'objet pour lequel vous voulez déclarer une propriété, y est une variable privée non visible en dehors du constructeur):

// getters and setters
var d = Date.prototype;
Object.defineProperty(d, "year", {
    get: function() {return this.getFullYear() },
    set: function(y) { this.setFullYear(y) }
});

Il définit la propriété d.year via get et set fonctions - si vous ne spécifiez pas set, alors la propriété est en lecture seule et ne peut pas être modifiée (soyez conscient que vous n'obtiendrez pas d'erreur si vous essayez de la définir, mais cela n'a aucun effet). Chaque propriété a les attributs writable, configurable (permettre de changer après la déclaration) et enumerable (permet de l'utiliser comme énumérateur), qui sont par défaut false. Vous pouvez les définir via defineProperty dans le 3ème paramètre, par ex. enumerable: true.

Ce qui est également valide est cette syntaxe:

// getters and setters - alternative syntax
var obj = { a: 7, 
            get b() {return this.a + 1;}, 
            set c(x) {this.a = x / 2}
        };

qui définit une propriété lisible / inscriptible a, une propriété en lecture seule b et une propriété en écriture seule c, à travers laquelle la propriété a peut être consulté.

Usage:

console.log(obj.a); console.log(obj.b); // output: 7, 8
obj.c=40;
console.log(obj.a); console.log(obj.b); // output: 20, 21

Remarques: 

Pour éviter un comportement inattendu dans le cas où vous avez oublié le new mot-clé, je suggère que vous ajoutiez ce qui suit à la fonction Podcast:

// instantiation helper
function Podcast() {
    if(false === (this instanceof Podcast)) {
        return new Podcast();
    }
// [... same as above ...]
};

Maintenant, les deux instanciations suivantes fonctionneront comme prévu:

var podcast = new Podcast(); // normal usage, still allowed
var podcast = Podcast();     // you can omit the new keyword because of the helper

La nouvelle instruction crée un nouvel objet et copie toutes les propriétés et méthodes, c'est-à-dire

var a=new Podcast();
var b=new Podcast();
a.title="a"; b.title="An "+b.title;
console.log(a.title); // "a"
console.log(b.title); // "An Astronomy Cast"

Notez aussi, que dans certaines situations, il peut être utile d'utiliser return instruction dans la fonction constructeur Podcast renvoyer un objet personnalisé protégeant des fonctions sur lesquelles la classe s'appuie en interne mais qui doivent être exposées. Ceci est expliqué plus en détail au chapitre 2 (Objets) de la série d'articles.

Tu peux dire ça a et b hériter de Podcast. Maintenant, que faire si vous voulez ajouter une méthode à Podcast qui s'applique à tous après a et b ont été instanciés? Dans ce cas, utilisez le .prototype comme suit:

Podcast.prototype.titleAndLink = function() {
    return this.title + " [" + this.link + "]";
};

Appelez maintenant a et b encore:

console.log(a.titleAndLink()); // "a [http://www.astronomycast.com]"
console.log(b.titleAndLink()); // "An Astronomy Cast [http://www.astronomycast.com]"

Vous pouvez trouver plus de détails sur les prototypes ici. Si vous voulez faire plus d'héritage, je suggère de regarder dans ce.


le série d'articles J'ai mentionné ci-dessus sont hautement recommandé à lire, ils incluent également les sujets suivants:

  1. Les fonctions
  2. Objets
  3. Prototypes
  4. Application de nouvelles fonctions sur le constructeur
  5. Levage
  6. Insertion automatique du point-virgule
  7. Propriétés et méthodes statiques

Remarque que le insertion automatique de point-virgule "feature" de JavaScript (comme mentionné au point 6.) est très souvent responsable de causer des problèmes étranges dans votre code. Par conséquent, je préférerais plutôt le considérer comme un bug que comme une fonctionnalité.

Si vous voulez en savoir plus, ici est un très intéressant Article MSDN à propos de ces sujets, certains d'entre eux décrits ici fournissent encore plus de détails.

Quel est intéressant à lire aussi (couvrant également les sujets mentionnés ci-dessus) sont les articles de la MDN JavaScript Guide:

Si vous voulez savoir comment émuler c # out paramètres (comme dans DateTime.TryParse(str, out result)) en JavaScript, vous pouvez trouver exemple de code ici.


Ceux d'entre vous qui sont travailler avec IE (qui n'a pas de console pour JavaScript sauf si vous ouvrez les outils de développement en utilisant F12 et ouvrez l'onglet de la console) pourrait trouver l'extrait suivant utile. Cela vous permet d'utiliser console.log(msg); comme utilisé dans les exemples ci-dessus. Il suffit de l'insérer avant le Podcast fonction.

Pour votre commodité, voici le code ci-dessus dans un extrait de code unique complet:

let console = { log: function(msg) {  
  let canvas = document.getElementById("log"), br = canvas.innerHTML==="" ? "" : "<br/>";
  canvas.innerHTML += (br + (msg || "").toString());
}};

console.log('For details, see the explaining text');

function Podcast() {

  // with this, you can instantiate without new (see description in text)
  if (false === (this instanceof Podcast)) {
    return new Podcast();
  }

  // private variables
  var _somePrivateVariable = 123;

  // object properties
  this.title = 'Astronomy Cast';
  this.description = 'A fact-based journey through the galaxy.';
  this.link = 'http://www.astronomycast.com';

  this.immutableProp = function() {
    return _somePrivateVariable;
  }

  // object function
  this.toString = function() {
    return 'Title: ' + this.title;
  }
};

// static property
Podcast.FILE_EXTENSION = 'mp3';
// static function
Podcast.download = function(podcast) {
  console.log('Downloading ' + podcast + ' ...');
};


// access static properties/functions
Podcast.FILE_EXTENSION; // 'mp3'
Podcast.download('Astronomy cast'); // 'Downloading Astronomy cast ...'

// access object properties/functions
var podcast = new Podcast();
podcast.title = 'The Simpsons';
console.log(podcast.toString()); // Title: The Simpsons
console.log(podcast.immutableProp()); // 123

// getters and setters
var d = Date.prototype;
Object.defineProperty(d, "year", {
  get: function() {
    return this.getFullYear()
  },
  set: function(y) {
    this.setFullYear(y)
  }
});

// getters and setters - alternative syntax
var obj = {
  a: 7,
  get b() {
    return this.a + 1;
  },
  set c(x) {
    this.a = x / 2
  }
};

// usage:
console.log(obj.a); console.log(obj.b); // output: 7, 8
obj.c=40;
console.log(obj.a); console.log(obj.b); // output: 20, 21

var a=new Podcast();
var b=new Podcast();
a.title="a"; b.title="An "+b.title;
console.log(a.title); // "a"
console.log(b.title); // "An Astronomy Cast"

Podcast.prototype.titleAndLink = function() {
    return this.title + " [" + this.link + "]";
};
    
console.log(a.titleAndLink()); // "a [http://www.astronomycast.com]"
console.log(b.titleAndLink()); // "An Astronomy Cast [http://www.astronomycast.com]"
<div id="log"></div>


Remarques: 

  • Quelques bons conseils, astuces et recommandations sur la programmation JavaScript en général, vous pouvez trouver ici (meilleures pratiques JavaScript) et là ('var' contre 'let'). Aussi recommandé est cet article à propos des typecasts implicites (coercition).

  • Un moyen pratique d'utiliser les classes et de les compiler en JavaScript est le TypeScript. Voici une aire de jeux où vous pouvez trouver quelques exemples vous montrant comment cela fonctionne. Même si vous n'utilisez pas TypeScript pour le moment, vous pouvez jeter un coup d'œil car vous pouvez comparer TypeScript avec le résultat JavaScript dans une vue côte à côte. La plupart des exemples sont simples, mais il y a aussi un exemple Raytracer que vous pouvez essayer instantanément. Je recommande en particulier de regarder dans les exemples "Using Classes", "Using Inheritance" et "Using Generics" en les sélectionnant dans la combobox - ce sont de jolis modèles que vous pouvez utiliser instantanément en JavaScript.

  • Atteindre encapsulation de variables locales, fonctions etc en JavaScript, je suggère d'utiliser un modèle comme celui-ci (JQuery utilise la même technique):

<html>
<head></head>
<body><script>
    'use strict';
    // module pattern (self invoked function)
    const myModule = (function(context) { 
    // to allow replacement of the function, use 'var' otherwise keep 'const'

      // put variables and function with local module scope here:
      var print = function(str) {
        if (str !== undefined) context.document.write(str);
        context.document.write("<br/><br/>");
        return;
      }
      // ... more variables ...

      // main method
      var _main = function(title) {

        if (title !== undefined) print(title);
        print("<b>last modified:&nbsp;</b>" + context.document.lastModified + "<br/>");        
        // ... more code ...
      }

      // public methods
      return {
        Main: _main
        // ... more public methods, properties ...
      };

    })(this);

    // use module
    myModule.Main("<b>Module demo</b>");
</script></body>
</html>

Bien sûr, vous pouvez - et devriez - placer le code du script dans un fichier * .js distinct; C'est juste écrit en ligne pour garder l'exemple court.


26
2017-09-10 11:53



Réponse mise à jour:

Dans ECMAScript 6, vous pouvez créer des fonctions statiques en utilisant static mot-clé:

class Foo {

  static bar() {return 'I am static.'}

}

//`bar` is a property of the class
Foo.bar() // returns 'I am static.'

//`bar` is not a property of instances of the class
var foo = new Foo()
foo.bar() //-> throws TypeError

Les classes ES6 n'introduisent aucune nouvelle sémantique pour la statique. Vous pouvez faire la même chose dans ES5 comme ceci:

//constructor
var Foo = function() {}

Foo.bar = function() {
    return 'I am static.'
}

Foo.bar() // returns 'I am static.'

var foo = new Foo()
foo.bar() // throws TypeError

Vous pouvez attribuer à une propriété de Foo parce que dans JavaScript les fonctions sont des objets.


20
2018-03-27 01:09



L'exemple et l'explication suivants proviennent du livre Professional JavaScript for Web Developers 2ème édition de Nicholas Zakas. C'est la réponse que je cherchais donc j'ai pensé qu'il serait utile de l'ajouter ici.

(function () {
    var name = '';
    Person = function (value) {
        name = value;
    };
    Person.prototype.getName = function () {
        return name;
    };
    Person.prototype.setName = function (value) {
        name = value;
    };
}());
var person1 = new Person('Nate');
console.log(person1.getName()); // Nate
person1.setName('James');
console.log(person1.getName()); // James
person1.name = 'Mark';
console.log(person1.name); // Mark
console.log(person1.getName()); // James
var person2 = new Person('Danielle');
console.log(person1.getName()); // Danielle
console.log(person2.getName()); // Danielle

le Person constructeur dans cet exemple a accès au nom de la variable privée, tout comme le getName() et setName() méthodes En utilisant ce modèle, la variable name devient statique et sera utilisée par toutes les instances. Cela signifie appeler setName() sur une instance affecte toutes les autres instances. Appel setName() ou créer un nouveau Person instance définit la variable name à une nouvelle valeur. Cela entraîne toutes les instances à retourner la même valeur.


15
2017-12-13 08:26



Si vous voulez déclarer des variables statiques pour créer des constantes dans votre application, j'ai trouvé que suivre l'approche la plus simpliste

ColorConstants = (function()
{
    var obj = {};
    obj.RED = 'red';
    obj.GREEN = 'green';
    obj.BLUE = 'blue';
    obj.ALL = [obj.RED, obj.GREEN, obj.BLUE];
    return obj;
})();

//Example usage.
var redColor = ColorConstants.RED;

8
2018-04-02 22:05