Pregunta Variables estáticas en JavaScript


¿Cómo puedo crear variables estáticas en Javascript?


608
2017-10-08 04:31


origen


Respuestas:


Si proviene de un lenguaje orientado a objetos de tipo estático y basado en clases (como Java, C ++ o C #) Supongo que está intentando crear una variable o método asociado a un "tipo" pero no a una instancia.

Un ejemplo que utiliza un enfoque "clásico", con funciones de constructor tal vez podría ayudarlo a captar los conceptos de JavaScript OO básico:

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 se define en el objeto MyClass (que es una función) y no tiene nada que ver con sus instancias creadas, JavaScript trata las funciones como objetos de primera clase, por lo que al ser un objeto, puede asignar propiedades a una función.


790
2017-10-08 04:49



Puede aprovechar el hecho de que las funciones JS también son objetos, lo que significa que pueden tener propiedades.

Por ejemplo, citando el ejemplo dado en el artículo (ahora desaparecido) Variables estáticas 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 llama a esa función varias veces, verá que el contador se está incrementando.

Y esta es probablemente una solución mucho mejor que el hecho de que el espacio de nombres global está contaminado con una variable global.


Y aquí hay otra posible solución, basada en un cierre: Truco para usar variables estáticas 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.

Lo que le da el mismo tipo de resultado, excepto que esta vez, el valor incrementado se devuelve, en lugar de mostrarse.


492
2017-10-08 04:37



Lo haces a través de un IIFE (expresión de función invocada inmediatamente):

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

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

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

76
2018-03-30 10:02



puede usar arguments.callee para almacenar variables "estáticas" (esto también es útil en la función anónima):

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



He visto un par de respuestas similares, pero me gustaría mencionar que esta publicación lo describe mejor, así que me gustaría compartirlo con usted.

Aquí hay un código tomado de él, que he modificado para obtener un ejemplo completo que, con suerte, beneficia a la comunidad porque puede usarse como plantilla de diseño para las clases.

También responde tu pregunta:

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

Dado ese ejemplo, puede acceder al propiedades / función estáticas como sigue:

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

Y el propiedades / funciones del objeto simplemente como:

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

Nota que en podcast.immutableProp (), tenemos un cierre: La referencia a _somePrivateVariable se mantiene dentro de la función.

Incluso puedes definir getters y setters. Eche un vistazo a este fragmento de código (donde d es el prototipo del objeto para el que desea declarar una propiedad, y es una variable privada no visible fuera del constructor):

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

Define la propiedad d.year vía get y set funciones - si no especifica set, entonces la propiedad es de solo lectura y no se puede modificar (tenga en cuenta que no obtendrá un error si intenta establecerlo, pero no tiene ningún efecto). Cada propiedad tiene los atributos writable, configurable (permite cambiar después de la declaración) y enumerable (permite usarlo como enumerador), que son por defecto false. Puedes configurarlos a través de defineProperty en el 3er parámetro, p. enumerable: true.

Lo que también es válido es esta sintaxis:

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

que define una propiedad de lectura / escritura a, una propiedad de solo lectura b y una propiedad solo de escritura c, a través de qué propiedad a puede ser accedido.

Uso:

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

Notas: 

Para evitar un comportamiento inesperado en caso de que haya olvidado el new palabra clave, le sugiero que agregue lo siguiente a la función Podcast:

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

Ahora las dos instancias siguientes funcionarán como se espera:

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

La declaración 'nueva' crea un nuevo objeto y copia todas las propiedades y métodos, es decir

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"

Tenga en cuenta también, que en algunas situaciones puede ser útil usar return declaración en la función constructora Podcast para devolver un objeto personalizado que protege las funciones de las que la clase confía internamente pero que necesitan ser expuestas. Esto se explica con más detalle en el capítulo 2 (Objetos) de la serie de artículos.

Puedes decir eso a y b heredar de Podcast. Ahora, ¿qué sucede si desea agregar un método a Podcast que se aplica a todos ellos después de a y b han sido instanciados? En este caso, use el .prototype como sigue:

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

Ahora llama a y b de nuevo:

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

Puede encontrar más detalles sobre prototipos aquí. Si quieres hacer más herencia, te sugiero investigar esta.


los serie de artículos He mencionado anteriormente son muy recomendable para leer, incluyen también los siguientes temas:

  1. Funciones
  2. Objetos
  3. Prototipos
  4. Hacer cumplir lo nuevo en las funciones del constructor
  5. Levantamiento
  6. Inserción Automática de Semicolon
  7. Propiedades y métodos estáticos

Nota que el inserción automática de punto y coma "característica" de JavaScript (como se menciona en 6.) a menudo es responsable de causar problemas extraños en su código. Por lo tanto, preferiría considerarlo como un error que como una característica.

Si quieres leer más, aquí es bastante interesante Artículo de MSDN sobre estos temas, algunos de ellos descritos allí proporcionan aún más detalles.

Que es interesante de leer también (que cubren también los temas mencionados anteriormente) son aquellos artículos de la Guía de JavaScript de MDN:

Si quieres saber cómo emular c # out parámetros (como en DateTime.TryParse(str, out result)) en JavaScript, puedes encontrar código de muestra aquí.


Aquellos de ustedes que son trabajando con IE (que no tiene consola para JavaScript a menos que abra las herramientas de desarrollador usando F12 y abra la pestaña de la consola) podría resultar útil el siguiente fragmento de código. Te permite usar console.log(msg); como se usa en los ejemplos anteriores. Solo insértela antes de Podcast función.

Para su comodidad, aquí está el código anterior en un solo fragmento de código único:

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>


Notas: 

  • Algunos buenos consejos, consejos y recomendaciones acerca de la programación de JavaScript en general puede encontrar aquí (prácticas recomendadas de JavaScript) y allí ('var' versus 'let'). También se recomienda este artículo sobre typecasts implícitos (coerción).

  • Una forma conveniente de usar clases y compilarlas en JavaScript es TypeScript. Aquí hay un parque infantil donde puedes encontrar algunos ejemplos que te muestran cómo funciona. Incluso si no está utilizando TypeScript en este momento, puede echar un vistazo porque puede comparar TypeScript con el resultado de JavaScript en una vista de lado a lado. La mayoría de los ejemplos son simples, pero también hay un ejemplo de Raytracer que puedes probar al instante. Recomiendo especialmente buscar en los ejemplos "Uso de clases", "Uso de herencia" y "Uso de genéricos" seleccionándolos en el cuadro combinado; estas son buenas plantillas que puede usar al instante en JavaScript.

  • Conseguir encapsulación de variables locales, funciones, etc. en JavaScript, sugiero usar un patrón como el siguiente (JQuery usa la misma técnica):

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

Por supuesto, puede - y debe - poner el código del script en un archivo * .js separado; esto se acaba de escribir en línea para mantener el ejemplo corto.


26
2017-09-10 11:53



Respuesta actualizada:

En ECMAScript 6, puede crear funciones estáticas utilizando static palabra clave:

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

Las clases de ES6 no introducen ninguna semántica nueva para la estática. Puedes hacer lo mismo en ES5 de esta manera:

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

Puede asignar a una propiedad de Foo porque en JavaScript las funciones son objetos.


20
2018-03-27 01:09



El siguiente ejemplo y explicación proviene del libro Professional JavaScript for Web Developers 2nd Edition de Nicholas Zakas. Esta es la respuesta que estaba buscando, así que pensé que sería útil agregarla aquí.

(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

los Person constructor en este ejemplo tiene acceso al nombre de la variable privada, al igual que getName() y setName() métodos. Al usar este patrón, la variable de nombre se vuelve estática y se usará entre todas las instancias. Esto significa llamar setName() en una instancia afecta a todas las demás instancias. Vocación setName() o creando un nuevo Person instancia establece la variable de nombre a un nuevo valor. Esto hace que todas las instancias devuelvan el mismo valor.


15
2017-12-13 08:26



Si desea declarar variables estáticas para crear constantes en su aplicación, entonces encontré el siguiente como enfoque más simplista

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



Si está usando el nuevo sintaxis de clase entonces ahora puedes hacer lo siguiente:

    class MyClass {
      static get myStaticVariable() {
        return "some static variable";
      }
    }

    console.log(MyClass.myStaticVariable);

    aMyClass = new MyClass();
    console.log(aMyClass.myStaticVariable, "is undefined");

Esto efectivamente crea una variable estática en JavaScript.


8
2018-02-22 16:11



Hay otras respuestas similares, pero ninguna me atraía. Esto es lo que terminé con:

var nextCounter = (function () {
  var counter = 0;
  return function() {
    var temp = counter;
    counter += 1;
    return temp;
  };
})();

6
2018-02-08 03:34