JavaScript中的静态变量

如何在JavaScript中创建静态变量?

813468 次浏览

JavaScript中最接近静态变量的是全局变量——这只是一个声明在函数或对象字面量范围之外的变量:

var thisIsGlobal = 1;
function foo() {var thisIsNot = 2;}

您可以做的另一件事是将全局变量存储在对象文字中,如下所示:

var foo = { bar : 1 }

然后像这样访问变量:foo.bar

窗口级变量有点像静态变量,因为您可以使用直接引用,并且这些变量可用于应用程序的所有部分

您可能会利用JS函数也是对象的事实——这意味着它们可以具有属性。

例如,引用(现已消失)文章JavaScript中的静态变量中给出的示例:

function countMyself() {// Check to see if the counter has been initializedif ( typeof countMyself.counter == 'undefined' ) {// It has not... perform the initializationcountMyself.counter = 0;}
// Do something stupid to indicate the valuealert(++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.

这将获得相同类型的结果--不同的是,这一次,递增的值被返回,而不是显示。

Javascript中没有静态变量这样的东西。这种语言是基于原型的面向对象的,所以没有类,而是对象“复制”自己的原型。

您可以使用全局变量或原型(向原型添加属性)来模拟它们:

function circle(){}circle.prototype.pi=3.14159

如果您来自基于类的、静态类型的面向对象语言(如Java,C++或C#),我假设您正在尝试创建与“类型”关联但不与实例关联的变量或方法。

一个使用“经典”方法的示例,带有构造函数,可能可以帮助您了解基本的OO JavaScript概念:

function MyClass () { // constructor functionvar privateVariable = "foo";  // Private variable
this.publicVariable = "bar";  // Public variable
this.privilegedMethod = function () {  // Public Methodalert(privateVariable);};}
// Instance method will be available to all instances but only load once in memoryMyClass.prototype.publicMethod = function () {alert(this.publicVariable);};
// Static variable shared by all instancesMyClass.staticProperty = "baz";
var myInstance = new MyClass();

staticProperty在MyClass对象(它是一个函数)中定义,与它创建的实例无关,JavaScript将函数视为一级对象,因此作为一个对象,您可以为函数分配属性。

更新: ES6通过class关键字引入了声明类的能力。它是现有基于原型的继承的语法糖。

#0关键字允许您轻松定义类中的静态属性或方法。

让我们看看上面用ES6类实现的示例:

class MyClass {// class constructor, equivalent to// the function body of a constructorconstructor() {const privateVariable = 'private value'; // Private variable at the constructor scopethis.publicVariable = 'public value'; // Public property
this.privilegedMethod = function() {// Public Method with access to the constructor scope variablesconsole.log(privateVariable);};}
// Prototype methods:publicMethod() {console.log(this.publicVariable);}
// Static properties shared by all instancesstatic staticProperty = 'static value';
static staticMethod() {console.log(this.staticProperty);}}
// We can add properties to the class prototypeMyClass.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"

您可以使用arguments.callee来存储“静态”变量(这在匿名函数中也很有用):

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

以下示例和解释来自Nicholas Zakas的《专业JavaScript for Web Developers第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()); // Nateperson1.setName('James');console.log(person1.getName()); // Jamesperson1.name = 'Mark';console.log(person1.name); // Markconsole.log(person1.getName()); // Jamesvar person2 = new Person('Danielle');console.log(person1.getName()); // Danielleconsole.log(person2.getName()); // Danielle

此示例中的Person构造函数可以访问私有变量name,getName()setName()方法也是如此。使用此模式,name变量将变为静态并将在所有实例中使用。这意味着在一个实例上调用setName()会影响所有其他实例。调用setName()或创建新的Person实例将name变量设置为新值。这会导致所有实例返回相同的值。

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();

如果你想创建一个全局静态变量:

var my_id = 123;

将变量替换为以下内容:

Object.defineProperty(window, 'my_id', {get: function() {return 123;},configurable : false,enumerable : false});
{var statvar = 0;function f_counter(){var nonstatvar = 0;nonstatvar ++;statvar ++;return statvar + " , " + nonstatvar;}}alert(f_counter());alert(f_counter());alert(f_counter());alert(f_counter());

这只是我在某处学到的拥有静态变量的另一种方式。

您可以通过IIFE(立即调用的函数表达式)来执行:

var incr = (function () {var i = 1;
return function () {return i++;}})();
incr(); // returns 1incr(); // returns 2

如果您想声明静态变量以在应用程序中创建常量,那么我发现以下是最简单的方法

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;

还有另一种方法,在浏览这个线程后解决了我的需求。这取决于你想用“静态变量”实现什么。

全局属性ses sionStorage或localStorage允许数据在会话的生命周期内存储,或者无限期地存储更长的时间,直到分别被明确清除。这允许数据在页面/应用的所有窗口、框架、选项卡面板、弹出窗口等之间共享,并且比一个代码段中简单的“静态/全局变量”强大得多。

它避免了顶级全局变量的范围,生命周期,语义学,动力学等的所有麻烦,即Window.myglobal.不知道它的效率如何,但对于以适度速率访问的少量数据来说,这并不重要。

以“sessionStorage.mydata=任何东西”的形式轻松访问并以类似方式检索“JavaScript:最终指南,第六版”,David Flanagan,ISBN:978-0-596-80552-4,第20章,第20.1节。这可以通过简单的搜索轻松下载为PDF,或者在您的O'Reilly Safaribook订阅中(值得其重量的黄金)。

我见过几个类似的答案,但我想提一下这篇文章最好地描述了它,所以我想和你分享。

这是从中提取的一些代码,我对其进行了修改以获得一个完整的示例,希望能为社区带来好处,因为它可以用作类的设计模板。

它也回答你的问题:

function Podcast() {
// private variablesvar _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 immutablePropthis.immutableProp = function() {return _somePrivateVariable;}
// object functionthis.toString = function() {return 'Title: ' + this.title;}};
// static propertyPodcast.FILE_EXTENSION = 'mp3';// static functionPodcast.download = function(podcast) {console.log('Downloading ' + podcast + ' ...');};

给定该示例,您可以访问静态属性/函数,如下所示:

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

对象属性/函数只是:

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

说明在podcast.immutableProp()中,我们有一个闭包对_somePrivateVariable的引用保留在函数内部。

您甚至可以定义getters和setters。看看这个代码片段(其中d是您要为其声明属性的对象原型,y是在构造函数之外不可见的私有变量):

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

它通过getset函数定义了属性d.year-如果您不指定set,则该属性是只读的,无法修改(请注意,如果您尝试设置它,则不会收到错误,但它没有效果)。每个属性都有属性writableconfigurable(允许在声明后更改)和enumerable(允许将其用作枚举器),默认值为false。您可以通过第3参数中的defineProperty设置它们,例如enumerable: true

同样有效的是这个语法:

// getters and setters - alternative syntaxvar 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, 8obj.c=40;console.log(obj.a); console.log(obj.b); // output: 20, 21

备注:

为了避免在您忘记new关键字时出现意外行为,我建议您将以下内容添加到函数Podcast中:

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

现在,以下两个实例化都将按预期工作:

var podcast = new Podcast(); // normal usage, still allowedvar 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"

也请注意,在某些情况下,在构造函数Podcast中使用return语句来返回一个自定义对象来保护类内部依赖但需要公开的函数可能很有用。这在本系列文章的第2章(对象)中进一步解释。

你可以说ab继承自Podcast。现在,如果你想在ab被实例化后向Podcast添加一个适用于所有方法的方法,该怎么办?在这种情况下,使用.prototype如下:

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

现在再次调用ab

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

您可以找到有关原型这里的更多详细信息。如果您想进行更多继承,我建议查看这个


我上面提到的文章系列是要阅读的强烈推荐,它们还包括以下主题:

  1. 函数
  2. 对象
  3. 原型
  4. 在构造函数上强制执行新功能
  5. 吊装
  6. 自动分号插入
  7. 静态特性和方法

说明,JavaScript的自动插入分号“特性”(如6.)经常导致代码中出现奇怪的问题。因此,我宁愿将其视为bug而不是特性。

如果你想阅读更多,这里是关于这些主题的一个非常有趣的MSDN文章,其中一些描述提供了更多细节。

有趣的阅读也是什么(也涵盖了上面提到的主题)是MDN JavaScript指南中的那些文章:

如果你想知道如何在JavaScript中模拟c##0参数(如DateTime.TryParse(str, out result)),你可以找到这里的示例代码。


使用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 variablesvar _somePrivateVariable = 123;
// object propertiesthis.title = 'Astronomy Cast';this.description = 'A fact-based journey through the galaxy.';this.link = 'http://www.astronomycast.com';
this.immutableProp = function() {return _somePrivateVariable;}
// object functionthis.toString = function() {return 'Title: ' + this.title;}};
// static propertyPodcast.FILE_EXTENSION = 'mp3';// static functionPodcast.download = function(podcast) {console.log('Downloading ' + podcast + ' ...');};

// access static properties/functionsPodcast.FILE_EXTENSION; // 'mp3'Podcast.download('Astronomy cast'); // 'Downloading Astronomy cast ...'
// access object properties/functionsvar podcast = new Podcast();podcast.title = 'The Simpsons';console.log(podcast.toString()); // Title: The Simpsonsconsole.log(podcast.immutableProp()); // 123
// getters and settersvar d = Date.prototype;Object.defineProperty(d, "year", {get: function() {return this.getFullYear()},set: function(y) {this.setFullYear(y)}});
// getters and setters - alternative syntaxvar 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, 8obj.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>


Notes:

  • Some good tips, hints and recommendations about JavaScript programming in general you can find here (JavaScript best practices) and there ('var' versus 'let'). Also recommended is this article about implicit typecasts (coercion).

  • A convenient way to use classes and compile them into JavaScript is TypeScript. Here is a playground where you can find some examples showing you how it works.Even if you're not using TypeScript at the moment, you can have a look because you can compare TypeScript with the JavaScript result on a side-by-side view. Most examples are simple, but there is also a Raytracer example which you can try out instantly.I recommend especially looking into the "Using Classes", "Using Inheritance" and "Using Generics" examples by selecting them in the combobox - these are nice templates you can instantly use in JavaScript. Typescript is used with Angular.

  • To achieve encapsulation of local variables, functions etc in JavaScript, I suggest to use a pattern like the following (JQuery uses the same 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 methodvar _main = function(title) {
if (title !== undefined) print(title);print("<b>last modified:&nbsp;</b>" + context.document.lastModified + "<br/>");// ... more code ...}
// public methodsreturn {Main: _main// ... more public methods, properties ...};
})(this);
// use modulemyModule.Main("<b>Module demo</b>");</script></body></html>

当然,您可以并且应该将脚本代码放在单独的*.js文件中;这只是内联编写的,以保持示例简短。

自调用函数(也称为IIFE=立即调用函数表达式)是在这里更详细地描述

要在此处压缩所有类概念,请测试以下内容:

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 classesvar test_var = "super private";
//the only way to access the "super private" test_var is from herethis.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 coffeescriptclass Test#static@prop = "static"
#instanceconstructor:(prop) ->@prop = propconsole.log(@prop)
t = new Test('inst_prop');
console.log(Test.prop);

//this is how the above is translated in plain js by the CS compilerTest = (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);

使用使用jQuery的MVC网站时,我喜欢确保某些事件处理程序中的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);

如果你想使用原型,那么有一种方法

var p = function Person() {this.x = 10;this.y = 20;}p.prototype.counter = 0;var person1 = new p();person1.prototype = p.prototype;console.log(person1.counter);person1.prototype.counter++;var person2 = new p();person2.prototype = p.prototype;console.log(person2.counter);console.log(person1.counter);

这样做,您将能够从任何实例访问计数器变量,并且属性的任何更改都将立即反映!!

当我看到这个时,我记得JavaScript闭包…这是我如何做到的…

        function Increment() {var num = 0; // Here num is a private static variablereturn function () {return ++num;}}
var inc = new Increment();console.log(inc());//Prints 1console.log(inc());//Prints 2console.log(inc());//Prints 3

还有其他类似的答案,但没有一个对我有吸引力。这是我最后得到的:

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

更新答案:

ECMAScript 6中,您可以使用static关键字创建静态函数:

class Foo {
static bar() {return 'I am static.'}
}
//`bar` is a property of the classFoo.bar() // returns 'I am static.'
//`bar` is not a property of instances of the classvar foo = new Foo()foo.bar() //-> throws TypeError

ES6类不会为静态引入任何新的语义学。你可以在ES5中这样做:

//constructorvar 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中函数是对象。

因此,我从其他答案中看到的是,它们没有解决面向对象程序设计中静态属性的基本架构要求。

面向对象编程实际上有两种不同的风格一种是“基于类”(C++、C#Java等),另一种是“原型”(Javascript)。在基于类的语言中,“静态属性”应该与类而不是实例化的对象相关联。这个概念实际上在像Javascript这样的原型语言中更直观地工作,因为你只需将属性分配为父原型的值。

function MyObject() {};MyObject.prototype.staticAttribute = "some value";

并从这个构造函数实例化的每个对象访问它,就像这样…

var childObject1 = new MyObject(); // Instantiate a child objectvar childObject2 = new MyObject(); // Instantiate another child objectconsole.log(childObject.staticAttribute); // Access the static Attribute from child 1console.log(childObject.staticAttribute); // Access the static Attribute from child 2

现在,如果您继续更改MyObject.prototype.staticAttribute,更改将向下级联到立即继承它的子对象。

然而,有一些“陷阱”可能会显着破坏此属性的“静态”性质,或者只是留下安全漏洞……

首先确保将构造函数隐藏在Global命名空间中,将其包含在另一个函数(如jQuery就绪方法)中

 $(document).ready(function () {function MyObject() {// some constructor instructions};MyObject.prototype.staticAttribute = "some value";var childObject = new MyObject(); // instantiate child objectconsole.log(childObject.staticAttribute); // test attribute});

其次也是最后一点,即使你这样做,属性仍然可以从你自己脚本的任何其他部分编辑,所以可能是代码中的bug在其中一个子对象上写入属性并将其与父原型分离,所以如果你更改父属性,它将不再级联并更改子对象的静态属性。看看这个jsfiddle。在不同的场景中,我们可以Object.freeze(obj)停止对子对象的任何更改,或者我们可以在构造函数中设置setter和getter方法并访问闭包,这两者都有相关的复杂性。

在我看来,“静态属性”的基于类的想法与这种Javascript实现之间没有完美的相似之处。因此,我认为从长远来看,使用更Javascript友好的不同代码模式可能会更好。例如中央数据存储或缓存,甚至是专用的帮助对象来保存所有必要的静态变量。

我在任何答案中都没有看到这个想法,所以只是将其添加到列表中。如果它是重复的,请告诉我,我会删除它并支持另一个。

我在我的网站中创建了一种超级全局。由于我有几个js文件在每个页面加载时加载,并且有几十个其他js文件仅在某些页面上加载,我将所有“全局”函数放入一个全局变量中。

在我的第一个包含的“全局”文件的顶部是声明

var cgf = {}; // Custom global functions.

然后删除几个全局辅助函数

cgf.formBehaviors = function(){// My form behaviors that get attached in every page load.}

然后,如果我需要一个静态变量,我只需将其存储在范围之外,例如在文档就绪或行为附件之外。(我使用jQuery,但它应该在javascript中工作)

cgf.first = true;$.on('click', '.my-button', function(){// Don't allow the user to press the submit twice.if (cgf.first){// first time behavior. such as submit}cgf.first = false;}

这当然是一个全局的而不是静态的,但因为它在每次页面加载时都被重新初始化,所以它实现了相同的目的。

你可以这样想。在<body></body>中放置一个标签<p id='staticVariable'></p>并设置其visibility: hide

当然,您可以使用jQuery管理上一个标签内的文本。这个标签原则上成为您的静态变量。

在JavaScript中,没有术语或关键字静态,但我们可以将这些数据直接放入函数对象中(就像在任何其他对象中一样)。

function f() {f.count = ++f.count || 1 // f.count is undefined at firstalert("Call No " + f.count)}
f(); // Call No 1
f(); // Call No 2

您可以像下面这样在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 1Person.count = Person.count ? Person.count + 1 : 1;}
var p1 = new Person('User p1');console.log(p1.constructor.count);   // prints 1var p2 = new Person('User p2');console.log(p2.constructor.count);   // prints 2

您可以使用Person函数或任何实例为静态变量赋值:

// set static variable using instance of Personp1.constructor.count = 10;         // this change is seen in all the instances of Personconsole.log(p2.constructor.count); // prints 10
// set static variable using PersonPerson.count = 20;console.log(p1.constructor.count); // prints 20

对于私有静态变量,我是这样发现的:

function Class(){}
Class.prototype = new function(){_privateStatic = 1;this.get = function() { return _privateStatic; }this.inc = function() { _privateStatic++; }};
var o1 = new Class();var o2 = new Class();
o1.inc();
console.log(o1.get());console.log(o2.get()); // 2

试试这个:

如果我们定义一个属性并覆盖它的getter和setter以使用Function Object属性,那么理论上你可以在javascript中拥有一个静态变量

例如:

function Animal() {if (isNaN(this.totalAnimalCount)) {this.totalAnimalCount = 0;}this.totalAnimalCount++;};Object.defineProperty(Animal.prototype, 'totalAnimalCount', {get: function() {return Animal['totalAnimalCount'];},set: function(val) {Animal['totalAnimalCount'] = val;}});var cat = new Animal();console.log(cat.totalAnimalCount); //Will produce 1var dog = new Animal();console.log(cat.totalAnimalCount); //Will produce 2 and so on.

在JavaScript中,一切都是原始类型或对象。函数是对象-(键值对)。

当您创建一个函数时,您会创建两个对象。一个对象代表函数本身,另一个代表函数的原型。

函数基本上是在这个意义上具有属性的对象:

function name,arguments lengthand the functional prototype.

那么在哪里设置静态属性:两个位置,要么在函数对象内部,要么在函数原型对象内部。

这是一个使用new JavaScript关键字创建该结束的片段,甚至实例化了两个实例。

function C () { // functionvar 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 1var 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);

主要思想是实例i1i2使用相同的静态属性。

如果您使用的是新的类语法,那么您现在可以执行以下操作:

    class MyClass {static get myStaticVariable() {return "some static variable";}}
console.log(MyClass.myStaticVariable);
aMyClass = new MyClass();console.log(aMyClass.myStaticVariable, "is undefined");

这有效地在JavaScript中创建了一个静态变量。

关于ECMAScript 2015引入的class。其他答案并不完全清楚。

下面是一个示例,展示如何使用ClassName.var synthax创建静态varstaticVar

class MyClass {constructor(val) {this.instanceVar = val;MyClass.staticVar = 10;}}
var class1 = new MyClass(1);console.log(class1.instanceVar);      // 1console.log(class1.constructor.staticVar); // 10
// New instance of MyClass with another valuevar class2 = new MyClass(3);console.log(class1.instanceVar);      // 1console.log(class2.instanceVar);      // 3

要访问静态变量,我们使用.constructor属性,该属性返回对创建类的对象构造函数的引用。我们可以在两个创建的实例上调用它:

MyClass.staticVar = 11;console.log(class1.constructor.staticVar); // 11console.log(class2.constructor.staticVar); // 11 <-- yes it's static! :)
MyClass.staticVar = 12;console.log(class1.constructor.staticVar); // 12console.log(class2.constructor.staticVar); // 12

在JavaScript中,变量默认为静态示例

var x = 0;
function draw() {alert(x); //x+=1;}
setInterval(draw, 1000);

x的值每1000毫秒递增1
它将打印1,2,3等等

函数的/类只允许单个构造函数用于其对象范围。#0

  • 使用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)

Closures - closure's copies are function with preserved data.

  • Each closure's copies are created to a function with their own free values or references, Whenever you use function inside another function, a closure is used.
  • A closure in JavaScript is like maintaining a copy of all the local variables of its parent function by the innerFunctions.

      function closureFun( args ) {// Local variable that ends up within closurevar num = args;num++;return function() { console.log(num); }}var closure1 = closureFun( 5 );var closure2 = closureFun( 777 );closure1(); // 5closure2(); // 777closure2(); // 778closure1(); // 6

ES5 Function Classes: uses Object.defineProperty ( O, P, Attributes )

The Object.defineProperty() method defines a new property directly on an object, or modifies an existing property on an object, and returns the object.

Created some methods by using ``, So that every once can understand the function classes easily.

'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 Methodconsole.log( obj1 );    // Rectangle {id: "R_1", width: 50, height: 20}obj1.area;              // Area :  1000obj1.globalValue;       // GET ID :  77777obj1.globalValue = 88;  // SET ID :  88obj1.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 :  350obj2.globalValue;       // GET ID :  88obj2.globalValue = 999; // SET ID :  999obj2.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 :  777obj1.setStaticVar(7);   // SET Instance Method Parent Class Static Value :  7obj1.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 instancemyInstance.constructor.staticMethod();console.log('From Inside Class : ',myInstance.constructor.staticVar);
// calling from classMyClass.staticMethod();console.log('Class : ', MyClass.staticVar);

ES6类:ES2015类是基于原型的OO模式上的简单糖。拥有单一方便的声明形式使类模式更易于使用,并鼓励互操作性。类支持基于原型的继承、超级调用、实例和静态方法以及构造函数。

示例:参考我以前的文章。

我经常使用静态函数变量,很遗憾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函数_statics将设置为init()结果。

然后你可以这样做:

function f() {const _s = f.statics(() => ({ v1=3, v2=somefunc() });
if (_s.v1==3) { ++_s.v1; _s.v2(_s.v1); }}

将此与另一个正确答案的IIFE进行比较,这有一个缺点,即在每个函数调用中添加一个赋值和一个if,并向函数添加一个“_statics”成员,但是有一些优点:参数位于顶部而不是内部函数中,在内部函数代码中使用“静态”是显式的,带有“_s”前缀,并且总体上更容易查看和理解。

除此之外,目前有一个关于ECMA提案的草案(第二阶段提案),在类中引入了static公共字段。(私人领域被认为

使用提案中的示例,提议的static语法如下所示:

class CustomDate {// ...static epoch = new CustomDate(0);}

并且相当于其他人强调的以下内容:

class CustomDate {// ...}CustomDate.epoch = new CustomDate(0);

然后您可以通过CustomDate.epoch访问它。

您可以在proposal-static-class-features中跟踪新提案。


目前,Babel通过您可以使用的转换类属性插件支持此功能。此外,尽管仍在开发中,#0正在实现

有4种方法可以在JavaScript中模拟函数局部静态变量。

方法1:使用函数对象属性(旧浏览器支持)

function someFunc1(){if( !('staticVar' in someFunc1) )someFunc1.staticVar = 0 ;alert(++someFunc1.staticVar) ;}
someFunc1() ; //prints 1someFunc1() ; //prints 2someFunc1() ; //prints 3

方法2:使用闭包,变体1(旧浏览器支持)

var someFunc2 = (function(){var staticVar = 0 ;return function(){alert(++staticVar) ;}})()
someFunc2() ; //prints 1someFunc2() ; //prints 2someFunc2() ; //prints 3

方法3:使用闭包,变体2(旧浏览器也支持)

var someFunc3 ;with({staticVar:0})var someFunc3 = function(){alert(++staticVar) ;}
someFunc3() ; //prints 1someFunc3() ; //prints 2someFunc3() ; //prints 3

方法4:使用闭包,变体3(需要支持EcmaScript 2015)

{let staticVar = 0 ;function someFunc4(){alert(++staticVar) ;}}
someFunc4() ; //prints 1someFunc4() ; //prints 2someFunc4() ; //prints 3

方法4:严格模式

'use strict'{let staticVar = 0 ;var someFunc4 = function(){alert(++staticVar) ;} ;}
someFunc4() ; //prints 1someFunc4() ; //prints 2someFunc4() ; //prints 3

总结:

ES6/ES 2015中,class关键字与static关键字一起引入。请记住,这是javavcript体现的原型继承模型的语法糖。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
}
// to create static variables just create a property on the prototype of the classDog.prototype.breed = 'Pitbull';// So to define a static property we don't need the `static` keyword.
const fluffy = new Dog();const vicky = new Dog();console.log(fluffy.breed, vicky.breed);
// changing the static variable changes it on all the objectsDog.prototype.breed = 'Terrier';console.log(fluffy.breed, vicky.breed);

我通常使用这种方法有两个主要原因:

如果我想存储函数的本地值,我会使用“Local. x”、“Local. y”、“Local. TempData”等!

如果我想存储函数的静态值,我使用像“静态. o”,“静态. Info”,“Static.count”等!

[更新2]:相同的方法,但使用IIFE方法!

[更新1]:函数的“静态”和“本地”对象是通过预编辑脚本自动创建的!

我使用了原型,它是这样工作的:

class Cat {constructor() {console.log(Cat.COLLECTION_NAME);}}
Cat.COLLECTION_NAME = "cats";

或者使用静态getter:

class Cat {constructor() {console.log(Cat.COLLECTION_NAME);}
static get COLLECTION_NAME() {return "cats"}}

您可以使用static关键字定义JavaScript中的静态函数

class MyClass {static myStaticFunction() {return 42;}}
MyClass.myStaticFunction(); // 42

在撰写本文时,你仍然不能在类中定义静态属性(函数除外)。静态属性仍然是第三阶段提案,这意味着它们还不是JavaScript的一部分。然而,没有什么能阻止你像分配其他对象一样简单地分配给一个类:

class MyClass {}
MyClass.myStaticProperty = 42;
MyClass.myStaticProperty; // 42

最后注意:小心使用带有继承-所有继承的类共享对象的相同副本的静态对象。

我有一个通用的方法:

  • 像这样创建对象:stat_flags = {};
  • 在动态添加字段时使用它:flags.popup_save_inited = true
  • 下次询问对象中是否存在您需要的标志并执行您的逻辑

示例:

class ACTGeneratedPages {constructor(table_data, html_table_id) {this.flags = {};//static flags for any processes
//any your code here
}
call_popup(post_id) {
let _this = this;document.getElementById('act-popup-template').style.display = 'block';
if (!this.flags.popup_save_inited) {//second time listener will not be attacheddocument.querySelector('.act-modal-save').addEventListener('click', function (e) {//saving data code herereturn false;});}
this.flags.popup_save_inited = true;//set flag here}
}

ES6类支持静态函数的行为很像其他面向对象语言中的静态函数:

class MyClass {static myFunction() {return 42;}}
typeof MyClass.myFunction; // 'function'MyClass.myFunction(); // 42

一般静态属性仍然是第三阶段提案,这意味着你需要Babel的第三阶段预设才能使用它们。但是对于Babel,你可以这样做:

class MyClass {static answer = 42;}
MyClass.answer; // 42

2021更新

在2021年,您可以简单地使用static关键字

TC39从2021年4月开始将STATIC关键字迁移到第四阶段语言功能。第一个JS特性花了很长时间才成为JS语言的正式特性集,但是等待的原因是缺乏浏览器支持;主要浏览器现在支持静态关键字,以及公共静态字段和私有静态字段的开放季节。



下面是实现静态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语言特性的实现(点击这里)。
要阅读更多关于这个特性本身的信息,以及查看演示用于静态字段的语法的示例(点击这里)。

您可以在声明静态变量后重新分配函数

function IHaveBeenCalled() {console.log("YOU SHOULD ONLY SEE THIS ONCE");return "Hello World: "}function testableFunction(...args) {testableFunction=inner //reassign the functionconst prepend=IHaveBeenCalled()return inner(...args) //pass all arguments the 1st timefunction inner(num) {console.log(prepend + num);}}testableFunction(2) // Hello World: 2testableFunction(5) // Hello World: 5

这使用...args,速度较慢,有没有办法第一次使用父函数的范围而不是传递所有参数?


我的用例:

function copyToClipboard(...args) {copyToClipboard = inner //reassign the functionconst child_process = require('child_process')return inner(...args) //pass all arguments the 1st timefunction inner(content_for_the_clipboard) {child_process.spawn('clip').stdin.end(content_for_the_clipboard)}}

如果您想在作用域之外使用child_process,您可以将其分配给copyToClipboard的属性

function copyToClipboard(...args) {copyToClipboard = inner //reassign the functioncopyToClipboard.child_process = require('child_process')return inner(...args) //pass all arguments the 1st timefunction inner(content_for_the_clipboard) {copyToClipboard.child_process.spawn('clip').stdin.end(content_for_the_clipboard)}}