833 Stimmen

Statische Variablen in JavaScript

Wie kann ich statische Variablen in Javascript erstellen?

970voto

Christian C. Salvadó Punkte 763569

Wenn Sie aus einer klassenbasierten, statisch typisierten objektorientierten Sprache kommen (wie Java, C++ oder C#) Ich nehme an, dass Sie versuchen, eine Variable oder Methode zu erstellen, die mit einem "Typ", aber nicht mit einer Instanz verbunden ist.

Ein Beispiel, das einen "klassischen" Ansatz mit Konstruktorfunktionen verwendet, könnte Ihnen helfen, die Konzepte der grundlegenden OO-JavaScript zu verstehen:

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 im MyClass-Objekt (das eine Funktion ist) definiert ist und nichts mit den von ihm erzeugten Instanzen zu tun hat, behandelt JavaScript Funktionen als erstklassige Objekte Da es sich also um ein Objekt handelt, können Sie einer Funktion Eigenschaften zuweisen.

UPDATE : Mit ES6 wurde die Möglichkeit eingeführt Klassen deklarieren durch die class Schlüsselwort. Es ist ein Syntaxzucker gegenüber der bestehenden prototypbasierten Vererbung.

El static Stichwort können Sie auf einfache Weise statische Eigenschaften oder Methoden in einer Klasse definieren.

Schauen wir uns das obige Beispiel an, das mit ES6-Klassen implementiert wurde:

class MyClass {
  // class constructor, equivalent to
  // the function body of a constructor
  constructor() {
    const privateVariable = 'private value'; // Private variable at the constructor scope
    this.publicVariable = 'public value'; // Public property

    this.privilegedMethod = function() {
      // Public Method with access to the constructor scope variables
      console.log(privateVariable);
    };
  }

  // Prototype methods:
  publicMethod() {
    console.log(this.publicVariable);
  }

  // Static properties shared by all instances
  static staticProperty = 'static value';

  static staticMethod() {
    console.log(this.staticProperty);
  }
}

// We can add properties to the class prototype
MyClass.prototype.additionalMethod = function() {
  console.log(this.publicVariable);
};

var myInstance = new MyClass();
myInstance.publicMethod();       // "public value"
myInstance.additionalMethod(); // "public value"
myInstance.privilegedMethod(); // "private value"
MyClass.staticMethod();             // "static value"

613voto

Pascal MARTIN Punkte 384469

Sie könnten sich die Tatsache zunutze machen, dass JS-Funktionen auch Objekte sind - was bedeutet, dass sie Eigenschaften haben können.

Um das Beispiel aus dem (inzwischen verschwundenen) Artikel zu zitieren Statische Variablen in 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);
}

Wenn Sie diese Funktion mehrmals aufrufen, werden Sie sehen, dass der Zähler erhöht wird.

Und das ist wahrscheinlich eine viel bessere Lösung, als den globalen Namespace mit einer globalen Variablen zu verunreinigen.

Und hier ist eine weitere mögliche Lösung, basierend auf einer Schließung: [Trick, um statische Variablen in Javascript zu verwenden][2] :

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.

Damit erhält man das gleiche Ergebnis - nur dass diesmal der inkrementierte Wert zurückgegeben wird, anstatt ihn anzuzeigen.

131voto

khoomeister Punkte 4404

Sie tun dies durch einen IIFE (immediately invoked function expression):

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

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

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

45voto

Matt Punkte 23047

Ich habe eine Reihe ähnlicher Antworten gesehen, aber ich möchte erwähnen, dass diese Stelle beschreibt es am besten, deshalb möchte ich es mit Ihnen teilen.

Hier ist ein Teil des Codes, den ich modifiziert habe, um ein vollständiges Beispiel zu erhalten, das hoffentlich für die Gemeinschaft von Nutzen ist, da es als Designvorlage für Klassen verwendet werden kann.

Außerdem beantwortet Ihre Frage:

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 + ' ...');
};

In diesem Beispiel können Sie auf die statische Eigenschaften/Funktion wie folgt:

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

Und die Objekteigenschaften/Funktionen einfach als:

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

Hinweis dass wir in podcast.immutableProp() eine Verschluss : Der Verweis auf _somePrivateVariable wird innerhalb der Funktion aufbewahrt.

Sie können sogar definieren Getter und Setter . Werfen Sie einen Blick auf dieses Codeschnipsel (wobei d ist der Prototyp des Objekts, für das Sie eine Eigenschaft deklarieren möchten, y ist eine private Variable, die außerhalb des Konstruktors nicht sichtbar ist):

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

Sie definiert die Eigenschaft d.year über get y set Funktionen - wenn Sie nicht angeben set ist die Eigenschaft schreibgeschützt und kann nicht geändert werden (Sie erhalten keine Fehlermeldung, wenn Sie versuchen, sie zu setzen, aber es hat keine Auswirkungen). Jede Eigenschaft hat die Attribute writable , configurable (Änderung nach der Deklaration möglich) und enumerable (erlauben die Verwendung als Aufzählungszeichen), die standardmäßig sind false . Sie können sie einstellen über defineProperty im 3. Parameter, z.B. enumerable: true .

Was auch gilt, ist diese Syntax:

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

die eine lesbare/schreibbare Eigenschaft definiert a eine schreibgeschützte Eigenschaft b und eine schreibgeschützte Eigenschaft c durch die das Eigentum a zugegriffen werden kann.

Verwendung:

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

Anmerkungen:

Um unerwartetes Verhalten zu vermeiden, falls Sie die new Schlüsselwort, schlage ich vor, dass Sie die Funktion wie folgt ergänzen Podcast :

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

Jetzt funktionieren die beiden folgenden Instanzen wie erwartet:

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

Die 'new'-Anweisung erzeugt ein neues Objekt und kopiert alle Eigenschaften und Methoden, d.h.

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"

Beachten Sie auch, dass es in manchen Situationen sinnvoll sein kann, die return Anweisung in der Konstruktorfunktion Podcast um ein benutzerdefiniertes Objekt zurückzugeben, das Funktionen schützt, auf die sich die Klasse intern verlässt, die aber offengelegt werden müssen. Dies wird in Kapitel 2 (Objekte) der Artikelserie näher erläutert.

Sie können sagen, dass a y b erben von Podcast . Was ist nun, wenn Sie Podcast eine Methode hinzufügen möchten, die für alle Podcasts gilt, nachdem a y b veranlasst worden sind? Verwenden Sie in diesem Fall die .prototype wie folgt:

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

Jetzt anrufen a y b wieder:

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

Weitere Informationen über Prototypen finden Sie unter aquí . Wenn Sie mehr vererben wollen, schlage ich vor, dass Sie sich mit este .


El Artikelserie die ich oben erwähnt habe, sind sehr empfehlenswert zu lesen, umfassen sie auch die folgenden Themen:

  1. Funktionen
  2. Objekte
  3. Prototypen
  4. Erzwingen von New bei Konstruktorfunktionen
  5. Heben
  6. Automatische Semikoloneinfügung
  7. Statische Eigenschaften und Methoden

Hinweis dass die automatische Einfügung von Semikolons "Feature" von JavaScript (wie in 6. erwähnt) ist sehr oft verantwortlich für seltsame Probleme in Ihrem Code verursachen. Daher würde ich es eher als Fehler denn als Funktion betrachten.

Wenn Sie mehr lesen möchten, aquí ist eine recht interessante MSDN-Artikel zu diesen Themen, einige der dort beschriebenen bieten noch mehr Details.

Was ist interessant zu lesen (die ebenfalls die oben genannten Themen abdecken) sind die Artikel aus der MDN JavaScript-Anleitung :

Wenn Sie wissen möchten, wie man c# emulieren out Parameter (wie in DateTime.TryParse(str, out result) ) in JavaScript, finden Sie Beispielcode hier.


Diejenigen von Ihnen, die Arbeiten mit IE (das keine Konsole für JavaScript hat, es sei denn, Sie öffnen die Entwicklerwerkzeuge mit F12 und öffnen Sie die Registerkarte "Konsole") könnte das folgende Snippet nützlich sein. Es ermöglicht Ihnen die Verwendung von console.log(msg); wie in den obigen Beispielen verwendet. Fügen Sie sie einfach vor der Podcast Funktion.

Zu Ihrer Erleichterung finden Sie hier den obigen Code in einem einzigen vollständigen Codeschnipsel:

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>

Anmerkungen:

  • Einige gute Tipps, Hinweise und Empfehlungen zur JavaScript-Programmierung im Allgemeinen finden Sie hier (bewährte JavaScript-Verfahren) y dort ('var' versus 'let') . Empfehlenswert ist auch dieser Artikel über implizite Typecasts (Zwang) .

  • Ein bequemer Weg, um Klassen zu verwenden und sie in JavaScript zu kompilieren, ist TypeScript. Hier ist ein Spielplatz wo Sie einige Beispiele finden können, die Ihnen zeigen, wie es funktioniert. Auch wenn Sie TypeScript im Moment nicht verwenden, können Sie sich die Beispiele ansehen, denn Sie können TypeScript mit dem JavaScript-Ergebnis in einer Side-by-Side-Ansicht vergleichen. Die meisten Beispiele sind einfach, aber es gibt auch ein Raytracer-Beispiel, das Sie sofort ausprobieren können. Ich empfehle besonders, sich die Beispiele "Using Classes", "Using Inheritance" und "Using Generics" anzusehen, indem man sie in der Combobox auswählt - das sind schöne Vorlagen, die man sofort in JavaScript verwenden kann. Typescript wird verwendet mit Winklig.

  • Zu erreichen Verkapselung von lokalen Variablen, Funktionen usw. in JavaScript, schlage ich vor, ein Muster wie das folgende zu verwenden (JQuery verwendet die gleiche Technik):

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

Natürlich können - und sollten - Sie den Skriptcode in einem separaten *.js Datei; dies wird nur inline geschrieben, um das Beispiel kurz zu halten.

Selbstaufrufende Funktionen (auch bekannt als IIFE = Immediately Invoked Function Expression) sind hier ausführlicher beschrieben .

41voto

gpilotino Punkte 12495

Können Sie arguments.callee verwenden, um "statische" Variablen zu speichern (dies ist auch in anonymen Funktionen nützlich):

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

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X