Статические переменные в JavaScript
Как я могу создать статические переменные в Javascript?
44 ответа
Если вы исходите из объектно-ориентированного языка на основе классов, статически типизированного (например, 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 обрабатывает функции как объекты первого класса, поэтому, будучи объектом, вы можете назначать свойства функции.
Вы можете воспользоваться тем фактом, что функции 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.
Который дает вам такой же результат - за исключением того, что на этот раз увеличенное значение возвращается, а не отображается.
Вы делаете это через IIFE (сразу вызывается выражение функции):
var incr = (function () {
var i = 1;
return function () {
return i++;
}
})();
incr(); // returns 1
incr(); // returns 2
Я видел пару похожих ответов, но я хотел бы отметить, что этот пост описывает это лучше всего, поэтому я хотел бы поделиться им с вами.
Вот некоторый код, взятый из него, который я модифицировал, чтобы получить полный пример, который, мы надеемся, принесет пользу сообществу, потому что его можно использовать как шаблон дизайна для классов.
Это также отвечает на ваш вопрос:
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
в 3-м параметре, например 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]"
Вы можете найти более подробную информацию о прототипах здесь. Если вы хотите сделать больше наследства, я предлагаю изучить это.
Рекомендуется прочитать серию статей, о которых я упоминал выше, и они также включают следующие темы:
- функции
- Объекты
- Прототипы
- Внедрение новых функций конструктора
- Подъемно
- Автоматическая вставка точки с запятой
- Статические свойства и методы
Обратите внимание, что функция " автоматической" вставки точек с запятой в JavaScript (как упоминалось в 6.) очень часто вызывает странные проблемы в вашем коде. Следовательно, я скорее расценил бы это как ошибку, чем как особенность.
Если вы хотите прочитать больше, вот довольно интересная статья MSDN на эти темы, некоторые из них, описанные там, предоставляют еще больше деталей.
Также интересно прочитать ( в том числе и по темам, упомянутым выше) эти статьи из Руководства по MDN JavaScript:
Если вы хотите знать, как эмулировать 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" против "let"). Также рекомендуется эта статья о неявных типах (принуждение).
Удобный способ использовать классы и скомпилировать их в JavaScript - это TypeScript. Вот игровая площадка, где вы можете найти несколько примеров, показывающих, как это работает. Даже если вы не используете TypeScript в данный момент, вы можете посмотреть, потому что вы можете сравнить TypeScript с результатом JavaScript в параллельном представлении. Большинство примеров просты, но есть также пример Raytracer, который вы можете попробовать немедленно. Особенно рекомендую ознакомиться с примерами "Использование классов", "Использование наследования" и "Использование обобщенных элементов", выбрав их в выпадающем списке - это замечательные шаблоны, которые можно мгновенно использовать в JavaScript.
Чтобы добиться инкапсуляции локальных переменных, функций и т. Д. В 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: </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; это просто написано, чтобы пример был коротким.
Вы можете использовать arguments.callee для хранения "статических" переменных (это также полезно в анонимной функции):
function () {
arguments.callee.myStaticVar = arguments.callee.myStaticVar || 1;
arguments.callee.myStaticVar++;
alert(arguments.callee.myStaticVar);
}
Обновленный ответ:
В 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 функции являются объектами.
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();
Следующий пример и пояснения взяты из книги Николаса Закаса "Профессиональный 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
instance устанавливает переменную name в новое значение. Это заставляет все экземпляры возвращать одно и то же значение.
Если вы используете новый синтаксис класса, то теперь вы можете сделать следующее:
class MyClass {
static get myStaticVariable() {
return "some static variable";
}
}
console.log(MyClass.myStaticVariable);
aMyClass = new MyClass();
console.log(aMyClass.myStaticVariable, "is undefined");
Это эффективно создает статическую переменную в JavaScript.
Есть 4 способа эмулировать функционально-локальные статические переменные в Javascript.
Способ 1. Использование свойств объекта функции (поддерживается в старых браузерах)
function someFunc1(){
if( !('staticVar' in someFunc1) )
someFunc1.staticVar = 0 ;
alert(++someFunc1.staticVar) ;
}
someFunc1() ; //prints 1
someFunc1() ; //prints 2
someFunc1() ; //prints 3
Способ 2: использование замыкания, вариант 1 (поддерживается в старых браузерах)
var someFunc2 = (function(){
var staticVar = 0 ;
return function(){
alert(++staticVar) ;
}
})()
someFunc2() ; //prints 1
someFunc2() ; //prints 2
someFunc2() ; //prints 3
Способ 3: использование замыкания, вариант 2 (также поддерживается в старых браузерах)
var someFunc3 ;
with({staticVar:0})
var someFunc3 = function(){
alert(++staticVar) ;
}
someFunc3() ; //prints 1
someFunc3() ; //prints 2
someFunc3() ; //prints 3
Способ 4: использование замыкания, вариант 3 (требуется поддержка EcmaScript 2015)
{
let staticVar = 0 ;
function someFunc4(){
alert(++staticVar) ;
}
}
someFunc4() ; //prints 1
someFunc4() ; //prints 2
someFunc4() ; //prints 3
ОБНОВЛЕНИЕ 2021
В 2021 году вы можете просто использовать ключевое слово
TC39 переместил
STATIC
с АПРЕЛЯ 2021 г.,
чтобы стать языковой функцией Stage-4.static
JS имеет официальный набор языковых функций JS, однако ожидание было связано с отсутствием поддержки браузером; Основные браузеры теперь поддерживают ключевое слово static, и его сезон открыт для общедоступных статических полей и частных статических полей.
Ниже приведен обобщенный пример того, как выглядит новый способ реализации статических членов класса JavaScript.
class ColorFinder {
static #red = "#ff0000";
static #green = "#00ff00";
static #blue = "#0000ff";
static colorName(name) {
switch (name) {
case "red": return ColorFinder.#red;
case "blue": return ColorFinder.#blue;
case "green": return ColorFinder.#green;
default: throw new RangeError("unknown color");
}
}
// Somehow use colorName
}
Пример выше был взят из официального репозитория TC39, статические переменные (семантика)
Чтобы узнать больше о реализации этой новой функции языка JS (НАЖМИТЕ ЗДЕСЬ).
Чтобы узнать больше о самой функции, а также увидеть примеры, демонстрирующие синтаксис, используемый для статических полей (НАЖМИТЕ ЗДЕСЬ).
О class
представленный ECMAScript 2015. Другие ответы не совсем ясны.
Вот пример, показывающий, как создать статическую переменную staticVar
с ClassName
, var
synthax:
class MyClass {
constructor(val) {
this.instanceVar = val;
MyClass.staticVar = 10;
}
}
var class1 = new MyClass(1);
console.log(class1.instanceVar); // 1
console.log(class1.constructor.staticVar); // 10
// New instance of MyClass with another value
var class2 = new MyClass(3);
console.log(class1.instanceVar); // 1
console.log(class2.instanceVar); // 3
Для доступа к статической переменной мы используем .constructor
свойство, которое возвращает ссылку на функцию конструктора объекта, создавшего класс. Мы можем вызвать это на двух созданных экземплярах:
MyClass.staticVar = 11;
console.log(class1.constructor.staticVar); // 11
console.log(class2.constructor.staticVar); // 11 <-- yes it's static! :)
MyClass.staticVar = 12;
console.log(class1.constructor.staticVar); // 12
console.log(class2.constructor.staticVar); // 12
Если вы хотите объявить статические переменные для создания констант в вашем приложении, то я считаю следующий способ наиболее упрощенным
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;
Есть и другие похожие ответы, но ни один из них мне не понравился. Вот чем я закончил:
var nextCounter = (function () {
var counter = 0;
return function() {
var temp = counter;
counter += 1;
return temp;
};
})();
В дополнение к остальному, в настоящее время есть проект (предложение этапа 2) по предложениям ECMA, который вводит static
публичные поля в классах. ( частные поля были рассмотрены)
Используя пример из предложения, предложенный static
синтаксис будет выглядеть так:
class CustomDate {
// ...
static epoch = new CustomDate(0);
}
и быть эквивалентным следующему, что другие подчеркнули:
class CustomDate {
// ...
}
CustomDate.epoch = new CustomDate(0);
Вы можете получить к нему доступ через CustomDate.epoch
,
Вы можете отслеживать новое предложение в proposal-static-class-features
,
В настоящее время babel поддерживает эту функцию с помощью плагина свойств класса transform, который вы можете использовать. Кроме того, хотя все еще в процессе, V8
внедряет это.
В JavaScript ближе всего к статической переменной это глобальная переменная - это просто переменная, объявленная вне области действия функции или литерала объекта:
var thisIsGlobal = 1;
function foo() {
var thisIsNot = 2;
}
Другая вещь, которую вы могли бы сделать, это хранить глобальные переменные внутри литерала объекта, например:
var foo = { bar : 1 }
И затем получить доступ к переменным, как это: foo.bar
,
Вы можете создать статическую переменную в JavaScript, как показано ниже. Вот count
является статической переменной
var Person = function(name) {
this.name = name;
// first time Person.count is undefined, so it is initialized with 1
// next time the function is called, the value of count is incremented by 1
Person.count = Person.count ? Person.count + 1 : 1;
}
var p1 = new Person('User p1');
console.log(p1.constructor.count); // prints 1
var p2 = new Person('User p2');
console.log(p2.constructor.count); // prints 2
Вы можете присвоить значения статической переменной, используя Person
функция или любой из случаев:
// set static variable using instance of Person
p1.constructor.count = 10; // this change is seen in all the instances of Person
console.log(p2.constructor.count); // prints 10
// set static variable using Person
Person.count = 20;
console.log(p1.constructor.count); // prints 20
В JavaScript переменные являются статическими по умолчанию. Пример:
var x = 0;
function draw() {
alert(x); //
x+=1;
}
setInterval(draw, 1000);
Значение x увеличивается на 1 каждые 1000 миллисекунд
Напечатает 1,2,3 пр.
Если вы хотите создать глобальную статическую переменную:
var my_id = 123;
Замените переменную следующим:
Object.defineProperty(window, 'my_id', {
get: function() {
return 123;
},
configurable : false,
enumerable : false
});
Чтобы сжать все понятия класса здесь, проверьте это:
var Test = function() {
// "super private" variable, accessible only here in constructor. There are no real private variables
//if as 'private' we intend variables accessible only by the class that defines the member and NOT by child classes
var test_var = "super private";
//the only way to access the "super private" test_var is from here
this.privileged = function(){
console.log(test_var);
}();
Test.test_var = 'protected';//protected variable: accessible only form inherited methods (prototype) AND child/inherited classes
this.init();
};//end constructor
Test.test_var = "static";//static variable: accessible everywhere (I mean, even out of prototype, see domready below)
Test.prototype = {
init:function(){
console.log('in',Test.test_var);
}
};//end prototype/class
//for example:
$(document).ready(function() {
console.log('out',Test.test_var);
var Jake = function(){}
Jake.prototype = new Test();
Jake.prototype.test = function(){
console.log('jake', Test.test_var);
}
var jake = new Jake();
jake.test();//output: "protected"
});//end domready
Ну, еще один способ взглянуть на лучшие практики в этих вещах, это просто посмотреть, как coffeescript переводит эти концепции.
#this is coffeescript
class Test
#static
@prop = "static"
#instance
constructor:(prop) ->
@prop = prop
console.log(@prop)
t = new Test('inst_prop');
console.log(Test.prop);
//this is how the above is translated in plain js by the CS compiler
Test = (function() {
Test.prop = "static";
function Test(prop) {
this.prop = prop;
console.log(this.prop);
}
return Test;
})();
t = new Test('inst_prop');
console.log(Test.prop);
Есть другой подход, который решил мои требования после просмотра этой темы. Это зависит от того, чего именно вы хотите достичь с помощью "статической переменной".
Глобальное свойство sessionStorage или localStorage позволяет хранить данные в течение жизни сеанса или в течение неопределенного более длительного периода до явной очистки соответственно. Это позволяет обмениваться данными между всеми окнами, фреймами, панелями вкладок, всплывающими окнами и т. Д. Вашей страницы / приложения и является гораздо более мощным, чем простая "статическая / глобальная переменная" в одном сегменте кода.
Это позволяет избежать хлопот, связанных с областью действия, временем жизни, семантикой, динамикой и т. Д. Глобальных переменных верхнего уровня, например Window.myglobal. Не знаю, насколько это эффективно, но это не важно для скромных объемов данных, к которым обращаются с умеренной скоростью.
Легко доступен как "sessionStorage.mydata = что-нибудь" и извлекается аналогично. См. "JavaScript: Полное руководство, шестое издание", Дэвид Фланаган, ISBN: 978-0-596-80552-4, глава 20, раздел 20.1. Это легко загрузить в формате PDF с помощью простого поиска или в подписке O'Reilly Safaribooks (на вес золота).
Функции / классы допускают только один конструктор для своей области видимости. Function Hoisting, declarations & expressions
Функции, созданные с помощью конструктора Function, не создают замыканий для своих контекстов создания; они всегда создаются в глобальном масштабе.
var functionClass = function ( ) { var currentClass = Shape; _inherits(currentClass, superClass); function functionClass() { superClass.call(this); // Linking with SuperClass Constructor. // Instance Variables list. this.id = id; return this; } }(SuperClass)
Закрытия - копии закрытия являются функцией с сохраненными данными.
- Копии каждого замыкания создаются функцией с их собственными свободными значениями или ссылками. Когда вы используете функцию внутри другой функции, используется замыкание.
Закрытие в JavaScript похоже на сохранение копии всех локальных переменных своей родительской функции с помощью innerFunctions.
function closureFun( args ) { // Local variable that ends up within closure var num = args; num++; return function() { console.log(num); } } var closure1 = closureFun( 5 ); var closure2 = closureFun( 777 ); closure1(); // 5 closure2(); // 777 closure2(); // 778 closure1(); // 6
Классы функций ES5: использует Object.defineProperty ( O, P, Атрибуты)
Метод Object.defineProperty() определяет новое свойство непосредственно для объекта или изменяет существующее свойство объекта и возвращает объект.
Создал несколько методов с помощью ``, чтобы каждый раз мог легко понять классы функций.
'use strict';
var Shape = function ( superClass ) {
var currentClass = Shape;
_inherits(currentClass, superClass); // Prototype Chain - Extends
function Shape(id) { superClass.call(this); // Linking with SuperClass Constructor.
// Instance Variables list.
this.id = id; return this;
}
var staticVariablesJOSN = { "parent_S_V" : 777 };
staticVariable( currentClass, staticVariablesJOSN );
// Setters, Getters, instanceMethods. [{}, {}];
var instanceFunctions = [
{
key: 'uniqueID',
get: function get() { return this.id; },
set: function set(changeVal) { this.id = changeVal; }
}
];
instanceMethods( currentClass, instanceFunctions );
return currentClass;
}(Object);
var Rectangle = function ( superClass ) {
var currentClass = Rectangle;
_inherits(currentClass, superClass); // Prototype Chain - Extends
function Rectangle(id, width, height) { superClass.call(this, id); // Linking with SuperClass Constructor.
this.width = width;
this.height = height; return this;
}
var staticVariablesJOSN = { "_staticVar" : 77777 };
staticVariable( currentClass, staticVariablesJOSN );
var staticFunctions = [
{
key: 'println',
value: function println() { console.log('Static Method'); }
}
];
staticMethods(currentClass, staticFunctions);
var instanceFunctions = [
{
key: 'setStaticVar',
value: function setStaticVar(staticVal) {
currentClass.parent_S_V = staticVal;
console.log('SET Instance Method Parent Class Static Value : ', currentClass.parent_S_V);
}
}, {
key: 'getStaticVar',
value: function getStaticVar() {
console.log('GET Instance Method Parent Class Static Value : ', currentClass.parent_S_V);
return currentClass.parent_S_V;
}
}, {
key: 'area',
get: function get() {
console.log('Area : ', this.width * this.height);
return this.width * this.height;
}
}, {
key: 'globalValue',
get: function get() {
console.log('GET ID : ', currentClass._staticVar);
return currentClass._staticVar;
},
set: function set(value) {
currentClass._staticVar = value;
console.log('SET ID : ', currentClass._staticVar);
}
}
];
instanceMethods( currentClass, instanceFunctions );
return currentClass;
}(Shape);
// ===== ES5 Class Conversion Supported Functions =====
function defineProperties(target, props) {
console.log(target, ' : ', props);
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
function staticMethods( currentClass, staticProps ) {
defineProperties(currentClass, staticProps);
};
function instanceMethods( currentClass, protoProps ) {
defineProperties(currentClass.prototype, protoProps);
};
function staticVariable( currentClass, staticVariales ) {
// Get Key Set and get its corresponding value.
// currentClass.key = value;
for( var prop in staticVariales ) {
console.log('Keys : Values');
if( staticVariales.hasOwnProperty( prop ) ) {
console.log(prop, ' : ', staticVariales[ prop ] );
currentClass[ prop ] = staticVariales[ prop ];
}
}
};
function _inherits(subClass, superClass) {
console.log( subClass, ' : extends : ', superClass );
if (typeof superClass !== "function" && superClass !== null) {
throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
}
subClass.prototype = Object.create(superClass && superClass.prototype,
{ constructor: { value: subClass, enumerable: false, writable: true, configurable: true } });
if (superClass)
Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
}
Ниже приведен фрагмент кода для проверки каждого экземпляра, который имеет свою собственную копию элементов экземпляра и общих статических элементов.
var objTest = new Rectangle('Yash_777', 8, 7);
console.dir(objTest);
var obj1 = new Rectangle('R_1', 50, 20);
Rectangle.println(); // Static Method
console.log( obj1 ); // Rectangle {id: "R_1", width: 50, height: 20}
obj1.area; // Area : 1000
obj1.globalValue; // GET ID : 77777
obj1.globalValue = 88; // SET ID : 88
obj1.globalValue; // GET ID : 88
var obj2 = new Rectangle('R_2', 5, 70);
console.log( obj2 ); // Rectangle {id: "R_2", width: 5, height: 70}
obj2.area; // Area : 350
obj2.globalValue; // GET ID : 88
obj2.globalValue = 999; // SET ID : 999
obj2.globalValue; // GET ID : 999
console.log('Static Variable Actions.');
obj1.globalValue; // GET ID : 999
console.log('Parent Class Static variables');
obj1.getStaticVar(); // GET Instance Method Parent Class Static Value : 777
obj1.setStaticVar(7); // SET Instance Method Parent Class Static Value : 7
obj1.getStaticVar(); // GET Instance Method Parent Class Static Value : 7
Вызовы статических методов выполняются непосредственно в классе и не могут быть вызваны в экземплярах класса. Но вы можете выполнить вызовы для статических членов внутри экземпляра.
Используя синтаксис:
this.constructor.staticfunctionName();
class MyClass {
constructor() {}
static staticMethod() {
console.log('Static Method');
}
}
MyClass.staticVar = 777;
var myInstance = new MyClass();
// calling from instance
myInstance.constructor.staticMethod();
console.log('From Inside Class : ',myInstance.constructor.staticVar);
// calling from class
MyClass.staticMethod();
console.log('Class : ', MyClass.staticVar);
Классы ES6: классы ES2015 - это простой сахар по сравнению с основанным на прототипе шаблоном ОО. Наличие единой удобной декларативной формы облегчает использование шаблонов классов и способствует взаимодействию. Классы поддерживают наследование на основе прототипов, супер вызовы, экземпляры и статические методы и конструкторы.
Пример: см. Мой предыдущий пост.
Вы можете определять статические функции в JavaScript, используяstatic
ключевое слово:
class MyClass {
static myStaticFunction() {
return 42;
}
}
MyClass.myStaticFunction(); // 42
На момент написания этой статьи вы все еще не можете определять статические свойства (кроме функций) внутри класса. Статические свойства все еще находятся на стадии 3, что означает, что они еще не являются частью JavaScript. Однако ничто не мешает вам просто назначить класс, как любому другому объекту:
class MyClass {}
MyClass.myStaticProperty = 42;
MyClass.myStaticProperty; // 42
Последнее замечание: будьте осторожны при использовании статических объектов с наследованием - все унаследованные классы используют одну и ту же копию объекта.
Я использовал прототип, и так он работал:
class Cat extends Anima {
constructor() {
super(Cat.COLLECTION_NAME);
}
}
Cat.COLLECTION_NAME = "cats";
или используя статический геттер:
class Cat extends Anima {
constructor() {
super(Cat.COLLECTION_NAME);
}
static get COLLECTION_NAME() {
return "cats"
}
}
В JavaScript нет термина или ключевого слова static, но мы можем поместить такие данные непосредственно в объект функции (как в любом другом объекте).
function f() {
f.count = ++f.count || 1 // f.count is undefined at first
alert("Call No " + f.count)
}
f(); // Call No 1
f(); // Call No 2
В JavaScript все является либо примитивным типом, либо объектом. Функции являются объектами - (пары ключ-значение).
Когда вы создаете функцию, вы создаете два объекта. Один объект, который представляет саму функцию, а другой - прототип функции.
Функция в основном в этом смысле является объектом со свойствами:
function name,
arguments length
and the functional prototype.
Итак, где установить статическое свойство: два места, либо внутри объекта функции, либо внутри объекта-прототипа функции.
Вот фрагмент, который создает этот конец, даже создает два экземпляра, используя new
Ключевое слово JavaScript.
function C () { // function
var privateProperty = "42";
this.publicProperty = "39";
this.privateMethod = function(){
console.log(privateProperty);
};
}
C.prototype.publicMethod = function () {
console.log(this.publicProperty);
};
C.prototype.staticPrototypeProperty = "4";
C.staticProperty = "3";
var i1 = new C(); // instance 1
var i2 = new C(); // instance 2
i1.privateMethod();
i1.publicMethod();
console.log(i1.__proto__.staticPrototypeProperty);
i1.__proto__.staticPrototypeProperty = "2";
console.log(i2.__proto__.staticPrototypeProperty);
console.log(i1.__proto__.constructor.staticProperty);
i1.__proto__.constructor.staticProperty = "9";
console.log(i2.__proto__.constructor.staticProperty);
Основная идея заключается в том, что случаи i1
а также i2
используют одни и те же статические свойства.
Резюме:
В ES6
/ES 2015 class
Ключевое слово было введено с сопровождающим static
ключевое слово. Имейте в виду, что это синтаксический сахар по сравнению с прототипной моделью наследования, которую воплощает javavscript. static
Ключевое слово работает следующим образом:
class Dog {
static bark () {console.log('woof');}
// classes are function objects under the hood
// bark method is located on the Dog function object
makeSound () { console.log('bark'); }
// makeSound is located on the Dog.prototype object
}
В chrome devtools мы можем визуализировать это:
Теперь мы создали static
функция, которую мы можем выполнить с помощью ClassName.method()
Я часто использую статические переменные функции, и очень жаль, что в JS для этого нет встроенного механизма. Слишком часто я вижу код, в котором переменные и функции определены во внешней области видимости, даже если они просто используются внутри одной функции. Это безобразно, подвержено ошибкам и просто напрашивается на неприятности...
Я придумал следующий метод:
if (typeof Function.prototype.statics === 'undefined') {
Function.prototype.statics = function(init) {
if (!this._statics) this._statics = init ? init() : {};
return this._statics;
}
}
Это добавляет метод "статика" ко всем функциям (да, просто расслабьтесь об этом), при вызове он добавляет пустой объект (_statics) к объекту функции и возвращает его. Если функция инициализации предоставлена, _statics будет установлен в init() result.
Затем вы можете сделать:
function f() {
const _s = f.statics(() => ({ v1=3, v2=somefunc() });
if (_s.v1==3) { ++_s.v1; _s.v2(_s.v1); }
}
Сравнивая это с IIFE, который является другим правильным ответом, он имеет недостаток, заключающийся в добавлении одного присваивания и одного if при каждом вызове функции и добавлении члена функции _statics к функции, однако есть несколько преимуществ: аргументы есть в верхняя часть не во внутренней функции, использование "статического" во внутреннем коде функции явно с помощью "_s". префикс, и это в целом проще смотреть и понимать.
Работая с веб-сайтами MVC, использующими jQuery, я хочу убедиться, что действия AJAX в определенных обработчиках событий могут выполняться только после завершения предыдущего запроса. Я использую "статическую" переменную объекта jqXHR для достижения этой цели.
Учитывая следующую кнопку:
<button type="button" onclick="ajaxAction(this, { url: '/SomeController/SomeAction' })">Action!</button>
Я обычно использую IIFE, как это для моего обработчика кликов:
var ajaxAction = (function (jqXHR) {
return function (sender, args) {
if (!jqXHR || jqXHR.readyState == 0 || jqXHR.readyState == 4) {
jqXHR = $.ajax({
url: args.url,
type: 'POST',
contentType: 'application/json',
data: JSON.stringify($(sender).closest('form').serialize()),
success: function (data) {
// Do something here with the data.
}
});
}
};
})(null);
Что я вижу в других ответах, так это в том, что они не отвечают фундаментальным архитектурным требованиям статического атрибута в объектно-ориентированном программировании.
На самом деле объектно-ориентированное программирование имеет два разных стиля: один "на основе классов" (C++, C#, Java и т. Д.), Другой "прототип" (Javascript). В языках на основе классов предполагается, что "статический атрибут" связан с классом, а не с объектами, для которых он создан. Эта концепция на самом деле работает гораздо более интуитивно в прототипных языках, таких как Javascript, потому что вы просто назначаете атрибут как значение родительского прототипа, как это происходит.
function MyObject() {};
MyObject.prototype.staticAttribute = "some value";
И получить доступ к нему из каждого из объектов, который создается из этого конструктора, вот так...
var childObject1 = new MyObject(); // Instantiate a child object
var childObject2 = new MyObject(); // Instantiate another child object
console.log(childObject.staticAttribute); // Access the static Attribute from child 1
console.log(childObject.staticAttribute); // Access the static Attribute from child 2
Теперь, если вы идете вперед и измените MyObject.prototype.staticAttribute
изменение будет касаться дочерних объектов, которые немедленно наследуют его.
Однако есть несколько "ошибок", которые могут существенно подорвать "статическую" природу этого атрибута или просто оставить уязвимость безопасности...
Сначала убедитесь, что конструктор скрыт от глобального пространства имен, заключив его в другую функцию, например, готовый метод jQuery.
$(document).ready(function () {
function MyObject() {
// some constructor instructions
};
MyObject.prototype.staticAttribute = "some value";
var childObject = new MyObject(); // instantiate child object
console.log(childObject.staticAttribute); // test attribute
});
Во-вторых, и наконец, даже если вы сделаете это, атрибут все равно будет редактируемым из любой другой части вашего собственного скрипта, поэтому может случиться так, что ошибка в вашем коде записывает поверх атрибута одного из дочерних объектов и отсоединяет это из родительского прототипа, так что если вы измените родительский атрибут, он больше не будет каскадно и не изменит статический атрибут для дочернего объекта. Смотрите это jsfiddle. В разных сценариях мы могли Object.freeze(obj)
чтобы остановить любые изменения в дочернем объекте, или мы могли бы установить метод setter и getter в конструкторе и получить доступ к замыканию, оба из которых имеют связанные сложности.
Мне кажется, что нет идеального аналога между основанной на классе идеей "статического атрибута" и этой реализацией Javascript. Поэтому я думаю, что в конечном итоге было бы лучше использовать другой шаблон кода, более дружественный к Javascript. Например, центральное хранилище данных или кэш или даже выделенный вспомогательный объект для хранения всех необходимых статических переменных.