如何在Javascript中创建静态变量?
当前回答
我已经看到了一些类似的答案,但我想提到这篇文章描述得最好,所以我想和大家分享一下。
下面是从中提取的一些代码,我已经对其进行了修改,以获得一个完整的示例,这有望为社区带来好处,因为它可以用作类的设计模板。
它还回答了您的问题:
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=立即调用函数表达式)。
其他回答
函数的/类只允许其对象范围使用一个构造函数。函数提升、声明和表达式
使用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模式更简单。拥有一个方便的声明性表单可以使类模式更易于使用,并鼓励互操作性。类支持基于原型的继承、超级调用、实例和静态方法以及构造函数。
示例:参考我以前的帖子。
您可以使用arguments.callee存储“静态”变量(这在匿名函数中也很有用):
function () {
arguments.callee.myStaticVar = arguments.callee.myStaticVar || 1;
arguments.callee.myStaticVar++;
alert(arguments.callee.myStaticVar);
}
关于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
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语言特性的实现(单击此处)。
阅读更多关于该特性本身的信息,以及演示静态字段语法的示例(单击此处)。
以下示例和解释来自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变量设置为新值。这会导致所有实例返回相同的值。