Статические переменные в JavaScript - PullRequest
662 голосов
/ 08 октября 2009

Как я могу создать статические переменные в Javascript?

Ответы [ 38 ]

824 голосов
/ 08 октября 2009

Если вы работаете с объектно-ориентированным языком статического типа на основе классов (например, Java, C ++ или C #) Я предполагаю, что вы пытаетесь создать переменную или метод, связанный с "типом" но не к экземпляру.

Пример, использующий «классический» подход с функциями конструктора, может помочь вам понять основные принципы OO JavaScript:

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 определено в объекте MyClass (который является функцией) и не имеет ничего общего с созданными экземплярами. JavaScript обрабатывает функции как объекты первого класса , поэтому, будучи объектом, вы можете назначить свойства функции.

520 голосов
/ 08 октября 2009

Вы можете воспользоваться тем фактом, что функции JS также являются объектами - это означает, что они могут иметь свойства.

Например, цитируя пример, приведенный в (теперь исчезнувшей) статье Статические переменные в 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);
}

Если вы вызовете эту функцию несколько раз, вы увидите, что счетчик увеличивается.

И это, вероятно, гораздо лучшее решение, чем загрязнение глобального пространства имен глобальной переменной.


И вот еще одно возможное решение, основанное на замыкании: Хитрость в использовании статических переменных в 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.

Что дает вам такой же результат - за исключением того, что на этот раз увеличенное значение возвращается, а не отображается.

84 голосов
/ 30 марта 2013

Вы делаете это через IIFE (выражение, вызываемое немедленно):

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

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

incr(); // returns 1
incr(); // returns 2
39 голосов
/ 06 ноября 2009

вы можете использовать arguments.callee для хранения «статических» переменных (это также полезно в анонимной функции):

function () {
  arguments.callee.myStaticVar = arguments.callee.myStaticVar || 1;
  arguments.callee.myStaticVar++;
  alert(arguments.callee.myStaticVar);
}
28 голосов
/ 29 февраля 2012
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();
28 голосов
/ 10 сентября 2013

Я видел пару похожих ответов, но я хотел бы отметить, что этот пост описывает его лучше всего, поэтому я хотел бы поделиться им с вами.

Вот некоторый код, взятый из него, который я модифицировал, чтобы получить полный пример, который, мы надеемся, принесет пользу сообществу, потому что его можно использовать как шаблон дизайна для классов.

Он также отвечает на ваш вопрос:

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

Учитывая этот пример, вы можете получить доступ к статическим свойствам / функции следующим образом:

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

Обратите внимание , что в podcast.immutableProp () у нас есть замыкание : Ссылка на _somePrivateVariable сохраняется внутри функции.

Вы даже можете определить геттеры и сеттеры . Взгляните на этот фрагмент кода (где d - это прототип объекта, для которого вы хотите объявить свойство, y - закрытая переменная, не видимая за пределами конструктора):

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

Он определяет свойство d.year с помощью функций get и set - если вы не укажете set, то свойство доступно только для чтения и не может быть изменено (учтите, что вы не получите ошибку если вы пытаетесь установить его, но это не имеет никакого эффекта). Каждое свойство имеет атрибуты writable, configurable (позволяют изменять после объявления) и enumerable (позволяют использовать его в качестве перечислителя), которые по умолчанию false. Вы можете установить их с помощью defineProperty в третьем параметре, например, enumerable: true.

Также допустим синтаксис:

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

, которая определяет свойство для чтения / записи a, свойство только для чтения b и свойство только для записи c, через которое можно получить доступ к свойству a.

Использование:

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

Примечания:

Чтобы избежать неожиданного поведения в случае, если вы забыли ключевое слово new, я предлагаю добавить в функцию Podcast следующее:

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

Теперь оба следующих экземпляра будут работать как положено:

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

Оператор 'new' создает новый объект и копирует все свойства и методы, т.е.

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"

Обратите также внимание, , что в некоторых ситуациях может быть полезно использовать оператор return в функции конструктора Podcast, чтобы возвращать пользовательский объект, защищающий функции, на которые класс полагается, но которые необходимо быть разоблаченным Это объясняется далее в главе 2 (Объекты) серии статей.

Можно сказать, что a и b наследуются от Podcast. Теперь, что если вы хотите добавить в Podcast метод, который будет применяться ко всем из них после создания экземпляров a и b? В этом случае используйте .prototype следующим образом:

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

Теперь позвоните a и b снова:

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

Более подробную информацию о прототипах вы можете найти здесь . Если вы хотите больше наследовать, я советую изучить this .


Серия статей , о которой я упоминал выше, настоятельно рекомендуется прочитать , они также включают следующие темы:

  1. Функции
  2. Предметы
  3. Прототипы
  4. Применение новых функций в конструкторе
  5. Подъемно
  6. Автоматическая вставка точек с запятой
  7. Статические свойства и методы

Обратите внимание , что автоматическая вставка точки с запятой «особенность» JavaScript (как упомянуто в 6.) очень часто является причиной странных проблем в вашем коде , Следовательно, я бы скорее расценил это как ошибку, чем как особенность.

Если вы хотите узнать больше, здесь - довольно интересная MSDN статья об этих темах, некоторые из них, описанные там, предоставляют еще больше деталей.

Что еще интересно читать (также охватывающие упомянутые выше темы) - это статьи из MDN JavaScript Guide :

Если вы хотите узнать, как эмулировать параметры c # out (как в DateTime.TryParse(str, out result)) в JavaScript, вы можете найти образец кода здесь.


Те из вас, кто работает с IE (в котором нет консоли для JavaScript, если вы не откроете инструменты разработчика с помощью F12 и не откроете вкладку консоли) может оказаться полезным следующий фрагмент кода. Это позволяет использовать console.log(msg);, как в примерах выше. Просто вставьте его перед функцией Podcast.

Для вашего удобства приведенный выше код в одном полном фрагменте кода:

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>

Примечания:

  • Несколько хороших советов, советов и рекомендаций по программированию на JavaScript в целом вы можете найти здесь (лучшие практики JavaScript) и там ('var 'против' пусть ') . Также рекомендуется эта статья о неявных типах (принуждение) .

  • Удобный способ использовать классы и скомпилировать их в JavaScript: TypeScript. Вот игровая площадка , где вы можете найти несколько примеров, показывающих вам как это устроено. Даже если вы не используете TypeScript в данный момент, вы можете посмотреть, потому что вы можете сравнить TypeScript с результатом JavaScript в режиме бок о бок. Большинство примеров просты, но есть и пример Raytracer, который вы можете попробовать немедленно. Особенно рекомендую ознакомиться с примерами «Использование классов», «Использование наследования» и «Использование обобщенных элементов», выбрав их в выпадающем списке - это хорошие шаблоны, которые можно мгновенно использовать в JavaScript. Машинописный текст используется с Angular.

  • Для достижения инкапсуляции локальных переменных, функций и т. Д. В JavaScript, я предлагаю использовать шаблон, подобный следующему (JQuery использует ту же технику):

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

Конечно, вы можете - и должны - поместить код скрипта в отдельный файл *.js; это просто написано, чтобы пример был коротким.

24 голосов
/ 27 марта 2015

Обновленный ответ:

В ECMAScript 6 вы можете создавать статические функции, используя ключевое слово static:

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

Классы ES6 не вводят никакой новой семантики для статики. Вы можете сделать то же самое в ES5 следующим образом:

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

Вы можете присвоить свойству Foo, потому что в JavaScript функции являются объектами.

16 голосов
/ 13 декабря 2011

Следующий пример и объяснение взяты из книги Николаса Закаса «Профессиональный JavaScript для веб-разработчиков, 2-е издание». Это ответ, который я искал, поэтому я подумал, что было бы полезно добавить его сюда.

(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

Конструктор Person в этом примере имеет доступ к имени закрытой переменной, как и методы getName() и setName(). Используя этот шаблон, переменная имени становится статической и будет использоваться среди всех экземпляров. Это означает, что вызов setName() для одного экземпляра влияет на все остальные экземпляры. Вызов setName() или создание нового экземпляра Person устанавливает переменную name в новое значение. Это заставляет все экземпляры возвращать одно и то же значение.

10 голосов
/ 22 февраля 2017

Если вы используете новый синтаксис класса , то теперь вы можете делать следующее:

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

    console.log(MyClass.myStaticVariable);

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

Это эффективно создает статическую переменную в JavaScript.

8 голосов
/ 03 апреля 2013

Если вы хотите объявить статические переменные для создания констант в вашем приложении, то я нашел следующий наиболее упрощенный подход

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