如何在Javascript中创建静态变量?
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 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.
这会得到相同的结果——只是这次返回的是递增的值,而不是显示。
Javascript中没有静态变量。这种语言是基于原型的面向对象的,因此没有类,而是对象“复制”自己的原型。
您可以使用全局变量或原型(向原型添加属性)来模拟它们:
function circle(){
}
circle.prototype.pi=3.14159
如果您来自基于类的静态类型的面向对象语言(如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将函数视为一级对象,因此作为一个对象,可以将财产分配给函数。
UPDATE:ES6引入了通过class关键字声明类的能力。它是现有的基于原型的继承的语法糖。
static关键字允许您轻松定义类中的静态财产或方法。
让我们看看上面用ES6类实现的示例:
类MyClass{//类构造函数,等效于//构造函数的函数体构造器(){const privateVariable=“private value”;//构造函数范围内的私有变量this.publicVariable='公共值';//公共财产this.privilegedMethod=函数(){//具有构造函数范围变量访问权限的公共方法console.log(privateVariable);};}//原型方法:publicMethod(){console.log(this.publicVariable);}//所有实例共享的静态财产static staticProperty='静态值';静态静态方法(){console.log(this.staticProperty);}}//我们可以将财产添加到类原型中MyClass.prototype.additionalMethod=函数(){console.log(this.publicVariable);};var myInstance=新建MyClass();myInstance.publicMethod();//“公共价值”myInstance.additionalMethod();//“公共价值”myInstance.privilegedMethod();//“私人价值”MyClass.staticMethod();//“静态值”
您可以使用arguments.callee存储“静态”变量(这在匿名函数中也很有用):
function () {
arguments.callee.myStaticVar = arguments.callee.myStaticVar || 1;
arguments.callee.myStaticVar++;
alert(arguments.callee.myStaticVar);
}
以下示例和解释来自Nicholas Zakas的《Web开发人员专业JavaScript第二版》一书。这是我一直在寻找的答案,所以我认为在这里添加它会很有帮助。
(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()方法也是如此。使用此模式,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 1
incr(); // 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;
还有另一种方法,它解决了我浏览这个线程后的需求。这完全取决于您想要使用“静态变量”实现什么。
全局属性sessionStorage或localStorage允许在会话的生命周期内存储数据,或在明确清除之前存储不确定的更长时间。这允许在页面/应用程序的所有窗口、框架、选项卡面板、弹出窗口等之间共享数据,并且比一个代码段中的简单“静态/全局变量”功能强大得多。
它避免了顶级全局变量(如Window.myglobal)的范围、生存期、语义、动态等方面的所有麻烦。不知道它有多高效,但这对于以适度速度访问的少量数据来说并不重要。
轻松访问为“sessionStorage.mydata=anything”,并以类似方式检索。看见“JavaScript:最终指南,第六版”,David Flanagan,ISBN:978-0-596-80552-4,第20章,第20.1节。通过简单的搜索,或在O’Reilly Safaribooks订阅(价值黄金)中,可以轻松下载为PDF格式。
我已经看到了一些类似的答案,但我想提到这篇文章描述得最好,所以我想和大家分享一下。
下面是从中提取的一些代码,我已经对其进行了修改,以获得一个完整的示例,这有望为社区带来好处,因为它可以用作类的设计模板。
它还回答了您的问题:
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
console.log(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的引用保留在函数中。
您甚至可以定义getter和setter。看看下面的代码段(其中d是要为其声明属性的对象原型,y是构造函数外部不可见的私有变量):
// getters and setters
var d = Date.prototype;
Object.defineProperty(d, "year", {
get: function() {return this.getFullYear() },
set: function(y) { this.setFullYear(y) }
});
它通过get和set函数定义属性d.year-如果未指定set,则该属性是只读的,无法修改(请注意,如果尝试设置该属性,则不会出现错误,但没有任何效果)。每个属性都具有可写、可配置(允许在声明后更改)和可枚举(允许将其用作枚举器)的属性,这些属性默认为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
笔记:
为了避免在忘记新关键字时出现意外行为,我建议您在函数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"
还要注意,在某些情况下,使用构造函数Podcast中的return语句返回一个自定义对象保护类内部依赖但需要公开的函数可能会很有用。这将在本系列文章的第2章(对象)中进一步解释。
你可以说a和b继承自播客。现在,如果您想在Podcast中添加一个方法,该方法在a和b安装后适用于所有的Podcast,该怎么办?在这种情况下,使用.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.中所述)通常会导致代码中出现奇怪的问题。因此,我宁愿将其视为一个bug而不是一个特性。
如果您想阅读更多,这里有一篇关于这些主题的MSDN文章,其中一些描述提供了更多细节。
阅读MDN JavaScript指南中的文章也很有趣(也涵盖了上述主题):
JavaScript简介使用对象
如果您想知道如何在JavaScript中模拟c#out参数(如DateTime.TryParse(str,out result)),可以在这里找到示例代码。
使用IE的人(除非使用F12打开开发人员工具并打开控制台选项卡,否则IE没有JavaScript控制台)可能会发现以下代码片段很有用。它允许您使用console.log(msg);如在上述示例中所使用的。只需将其插入播客功能之前。
为了方便起见,下面是上面一个完整的代码片段中的代码:
让控制台={log:function(msg){let canvas=document.getElementById(“log”),br=canvas.innerHTML==“”?“”:“<br/>”;canvas.innerHTML+=(br+(msg||“”).toString());}};console.log('有关详细信息,请参阅解释文本');函数播客(){//使用此选项,您可以在没有新建的情况下进行实例化(请参阅文本中的描述)if(false==(播客的这个实例)){return new播客();}//私有变量var_somePrivateVariable=123;//对象财产this.title='天文学演员';this.description='基于事实的银河之旅';this.link='http://www.astronomycast.com';this.immutableProp=函数(){return _somePrivateVariable;}//目标函数this.toString=函数(){return'标题:'+this.Title;}};//静态特性播客.FILE_EXTENSION=“mp3”;//静态函数Podcast.download=函数(播客){console.log(“下载”+播客+“…”);};//访问静态财产/函数播客.FILE_EXTENSION;//'mp3'播客.下载('天文学广播');//'正在下载天文学演员…'//访问对象财产/函数var podcast=新播客();podcast.title=“辛普森一家”;console.log(podcast.toString());//标题:《辛普森一家》console.log(podco.immutableProp());//123//吸气器和settervar d=日期.协议类型;Object.defineProperty(d,“年”{获取:函数(){return this.getFullYear()},集合:函数(y){this.setFullYear(y)}});//getters和setters-替代语法变量obj={a: 7中,获取b(){返回此.a+1;},集合c(x){这个.a=x/2}};//用法:控制台日志(obj.a);console.log(obj.b);//输出:7,8物镜c=40;控制台日志(obj.a);console.log(obj.b);//输出:20,21var a=新播客();var b=新播客();a.title=“a”;b.title=“An”+b.title;console.log(a.title);//“a”console.log(b.title);//“天文学演员”Podcast.prototype.titleAndLink=函数(){return this.title+“[”+this.link+“]”;};console.log(a.titleAndLink());//“一个[http://www.astronomycast.com]"console.log(b.titleAndLink());//“天文学演员[http://www.astronomycast.com]"<div id=“log”></div>
笔记:
在这里(JavaScript最佳实践)和那里(“var”与“let”)都可以找到关于JavaScript编程的一些好的提示、提示和建议。还推荐这篇关于隐式类型转换(强制)的文章。使用类并将其编译为JavaScript的一种方便方法是TypeScript。这里是一个游乐场,你可以在那里找到一些例子,告诉你它是如何工作的。即使您目前没有使用TypeScript,也可以查看一下,因为您可以在并排视图中将TypeScript与JavaScript结果进行比较。大多数示例都很简单,但也有一个光线跟踪器示例,您可以立即尝试。我建议特别关注“使用类”、“使用继承”和“使用泛型”示例,方法是在组合框中选择它们-这些都是很好的模板,可以立即在JavaScript中使用。字体与Angular一起使用。为了在JavaScript中实现本地变量、函数等的封装,我建议使用如下模式(JQuery使用相同的技术):
<html><head></head><body><脚本>“使用严格”;//模块模式(自调用函数)const myModule=(函数(上下文){//要允许替换函数,请使用“var”,否则保留“const”//将变量和函数与本地模块范围放在此处:var print=函数(str){if(str!==undefined)context.document.write(str);context.document.write(“<br/><br/>”);回来}// ... 更多变量。。。//主要方法var _main=函数(标题){if(title!==未定义)打印(title);打印(“<b>上次修改时间:</b>”+context.document.lastModified+“<br/>”);// ... 更多代码。。。}//公共方法返回{主:_Main// ... 更多公共方法、财产。。。};})(本);//使用模块myModule.Main(“<b>模块演示</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 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
好吧,另一种了解这些方面最佳实践的方法是看看咖啡脚本是如何翻译这些概念的。
#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);
在使用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 variable
return function () {
return ++num;
}
}
var inc = new Increment();
console.log(inc());//Prints 1
console.log(inc());//Prints 2
console.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 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中,函数是对象。
所以我从其他答案中看到的是,它们没有解决面向对象编程中静态属性的基本架构要求。
面向对象编程实际上有两种不同的风格,一种是基于类的(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 ready方法)中,从全局命名空间中隐藏构造函数
$(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更友好的不同代码模式可能会更好。比如一个中央数据存储或缓存,甚至一个专用的帮助对象来保存所有必要的静态变量。
我没有在任何答案中看到这个想法,所以只是将其添加到列表中。如果是重复的,请告诉我,我会删除它并对另一个进行投票。
我在我的网站上创建了一种超级全球化。由于每次页面加载时都会加载几个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=“静态变量”></p>并设置其可见性:隐藏。
当然,您可以使用jquery来管理前一个标记中的文本。实际上,这个标签成为你的静态变量。
在JavaScript中,没有静态的术语或关键字,但我们可以将这些数据直接放到函数对象中(就像在任何其他对象中一样)。
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中创建一个静态变量,如下所示。这里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
对于私有静态变量,我是这样发现的:
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中使用一个静态变量
例如:
函数Animal(){if(isNaN(this.totalAnimalCount)){this.totalAnimalCount=0;}this.totalAnimationCount++;};Object.defineProperty(动画原型,“totalAnimalCount”{获取:函数(){return Animal['totalAnimalCount'];},集合:函数(val){动物['totalAnimalCount']=val;}});var cat=新动画();console.log(cat.totalAnimationCount)//将产生1var dog=新动画();console.log(cat.totalAnimationCount)//将产生2等。
在JavaScript中,任何东西都是原始类型或对象。函数是对象-(键值对)。
创建函数时,将创建两个对象。一个对象表示函数本身,另一个对象代表函数的原型。
从这个意义上讲,函数基本上是一个具有财产的对象:
function name,
arguments length
and the functional prototype.
因此,在何处设置静态属性:两个位置,要么在函数对象内部,要么在功能原型对象内部。
这里有一个代码片段,它使用新的JavaScript关键字创建了两个实例。
函数C(){//函数var privateProperty=“42”;this.publicProperty=“39”;this.privateMethod=函数(){console.log(privateProperty);};}C.prototype.publicMethod=函数(){console.log(this.publicProperty);};C.prototype.staticPrototypeProperty=“4”;C.staticProperty=“3”;var i1=新C();//实例1var i2=新C();//实例2i1.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使用相同的静态财产。
如果您正在使用新的类语法,那么现在可以执行以下操作:
类MyClass{静态获取myStaticVariable(){返回“一些静态变量”;}}console.log(MyClass.myStaticVariable);aMyClass=新建MyClass();console.log(aMyClass.myStaticVariable,“未定义”);
这有效地在JavaScript中创建了一个静态变量。
关于ECMAScript 2015引入的类。其他答案并不完全清楚。
下面是一个示例,演示如何使用ClassName.var synthax创建静态var-staticVar:
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
在JavaScript中,默认情况下变量是静态的。例子:
var x = 0;
function draw() {
alert(x); //
x+=1;
}
setInterval(draw, 1000);
x的值每1000毫秒递增1它将打印1,2,3等
函数的/类只允许其对象范围使用一个构造函数。函数提升、声明和表达式
使用Function构造函数创建的函数不会创建其创建上下文的闭包;它们总是在全局范围内创建的。var functionClass=函数(){var currentClass=形状;_继承(currentClass,superClass);function functionClass(){superClass.call(this);//与superClass构造函数链接。//实例变量列表。this.id=id;返回此;}}(特级)
闭包-闭包的副本是保存数据的函数。
每个闭包的副本都被创建到一个具有自己的自由值或引用的函数。每当您在另一个函数中使用函数时,都会使用闭包。JavaScript中的闭包就像innerFunctions维护其父函数的所有局部变量的副本。函数closureFun(参数){//结束于闭包内的局部变量var num=参数;num++;return函数(){console.log(num);}}var closure1=closureFun(5);var closure2=closureFun(777);closure1();//5.closure2();//777closure2();//778closure1();//6.
ES5函数类:使用Object.defineProperty(O,P,Attributes)
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类比基于原型的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”成员,但也有一些优点:参数位于顶部而不是内部函数中,在内部函数代码中使用“static”是显式的,带有“_s”前缀,总体上看和理解起来更简单。
除其他内容外,目前还有一个关于ECMA提案的草案(第2阶段提案),它在类中引入了静态公共字段。(考虑了私人领域)
使用提案中的示例,建议的静态语法如下所示:
class CustomDate {
// ...
static epoch = new CustomDate(0);
}
并等同于其他人强调的以下内容:
class CustomDate {
// ...
}
CustomDate.epoch = new CustomDate(0);
然后,您可以通过CustomDate.epoch访问它。
您可以在提案静态类特性中跟踪新提案。
目前,babel通过您可以使用的转换类财产插件支持此功能。此外,尽管仍在进行中,V8正在实现它。
在Javascript中有4种模拟函数局部静态变量的方法。
方法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
严格模式的方法4
'use strict'
{
let staticVar = 0 ;
var someFunc4 = function(){
alert(++staticVar) ;
} ;
}
someFunc4() ; //prints 1
someFunc4() ; //prints 2
someFunc4() ; //prints 3
摘要:
在ES6/ES 2015中,class关键字与附带的静态关键字一起引入。请记住,这是javavscript所体现的原型继承模型的语法糖。static关键字对方法的工作方式如下:
类狗{静态树皮(){console.log('wof');}//类是隐藏的函数对象//树皮方法位于Dog函数对象上makeSound(){console.log('bark');}//makeSound位于Dog.prototype对象上}//要创建静态变量,只需在类的原型上创建一个属性Dog.prototype.breed=“Pitbull”;//因此,要定义静态属性,我们不需要“static”关键字。const蓬松=新狗();const vicky=新狗();控制台.日志(绒毛.品种,vicky.品种);//更改静态变量会更改所有对象上的静态变量Dog.prototype.breed=“梗”;控制台.日志(绒毛.品种,vicky.品种);
我通常使用这种方法有两个主要原因:
如果我想存储函数的本地值,我会使用“local.x”、“local.y”、“local.TempData”等。。。!
如果我想存储函数的静态值,我会使用“static.o”、“static.Info”、“static.count”等。。。!
[Update2]:方法相同,但使用IIFE方法!
[Update1]:通过预编辑脚本自动创建函数的“静态”和“本地”对象!
我使用了原型,并以这种方式工作:
class Cat {
constructor() {
console.log(Cat.COLLECTION_NAME);
}
}
Cat.COLLECTION_NAME = "cats";
或使用静态吸气剂:
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 attached
document.querySelector('.act-modal-save').addEventListener('click', function (e) {
//saving data code here
return 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,你可以做到这一点:
class MyClass {
static answer = 42;
}
MyClass.answer; // 42
2021更新
在2021,您可以简单地使用static关键字
截至2021 4月,TC39将STATIC关键字移至第4阶段语言功能。将静态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语言特性的实现(单击此处)。
阅读更多关于该特性本身的信息,以及演示静态字段语法的示例(单击此处)。
可以在声明静态变量后重新分配函数
function IHaveBeenCalled() {
console.log("YOU SHOULD ONLY SEE THIS ONCE");
return "Hello World: "
}
function testableFunction(...args) {
testableFunction=inner //reassign the function
const prepend=IHaveBeenCalled()
return inner(...args) //pass all arguments the 1st time
function inner(num) {
console.log(prepend + num);
}
}
testableFunction(2) // Hello World: 2
testableFunction(5) // Hello World: 5
这使用。。。args比较慢,有没有办法第一次使用父函数的作用域而不是传递所有参数?
我的用例:
function copyToClipboard(...args) {
copyToClipboard = inner //reassign the function
const child_process = require('child_process')
return inner(...args) //pass all arguments the 1st time
function inner(content_for_the_clipboard) {
child_process.spawn('clip').stdin.end(content_for_the_clipboard)
}
}
如果要在作用域之外使用child_process,可以将其分配给copyToCclipboard的属性
function copyToClipboard(...args) {
copyToClipboard = inner //reassign the function
copyToClipboard.child_process = require('child_process')
return inner(...args) //pass all arguments the 1st time
function inner(content_for_the_clipboard) {
copyToClipboard.child_process.spawn('clip').stdin.end(content_for_the_clipboard)
}
}
推荐文章
- (深度)使用jQuery复制数组
- 你从哪里包含jQuery库?谷歌JSAPI吗?CDN吗?
- 为什么在Java中使用静态嵌套接口?
- 在setInterval中使用React状态钩子时状态不更新
- 使用JavaScript显示/隐藏'div'
- 使用JavaScript获取所选的选项文本
- AngularJS模板中的三元运算符
- 让d3.js可视化布局反应灵敏的最好方法是什么?
- 原型的目的是什么?
- 检查jquery是否使用Javascript加载
- 将camelCaseText转换为标题大小写文本
- 如何在JavaScript客户端截屏网站/谷歌怎么做的?(无需存取硬盘)
- 如何在JavaScript中遍历表行和单元格?
- jQuery map vs. each
- 自定义异常类型