JavaScript中的静态变量

Raj*_*jat 680 javascript variables static closures

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

CMS*_*CMS 837

如果你来自基于类的,静态类型的面向对象语言(如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();
Run Code Online (Sandbox Code Playgroud)

staticProperty在MyClass对象(它是一个函数)中定义并且与其创建的实例无关,JavaScript将函数视为第一类对象,因此作为对象,您可以为函数指定属性.

  • 嗨,我不确定我同意这一行//所有实例共享的静态变量'MyClass.staticProperty ="baz";' 至于_me_,推断你可以从'myInstance.staticProperty'找到baz,当然你不能. (17认同)
  • 推测`privilegedMethod`并不等同于OO中的私有方法,因为它似乎可以在MyClass的实例上调用?你的意思是它有特权,因为它可以访问`privateVariable`? (5认同)
  • 也许应该读取`MyClass.prototype.staticProperty ="baz";`或者更准确地说OO原则静态属性实际上应该被定义为匿名函数`MyClass.prototype.staticProperty = function(){return staticVar;所有实例都访问一个变量,这个变量也可以用setter改变. (5认同)
  • 不能使用`this.constructor`来访问"实例方法"中的静态变量吗?如果是的话,值得将其添加到答案中. (3认同)
  • 您还可以在示例中提及**静态函数**。 (2认同)

Pas*_*TIN 528

您可以利用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);
}
Run Code Online (Sandbox Code Playgroud)

如果您多次调用该函数,您将看到计数器正在递增.

这可能是一个比使用全局变量来规划全局命名空间更好的解决方案.


这是另一个可能的解决方案,基于一个闭包:在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.
Run Code Online (Sandbox Code Playgroud)

这会得到相同类型的结果 - 除此之外,返回增加的值而不是显示.

  • 作为一种捷径,你可以做`countMyself.counter = countMyself.counter || initial_value;`如果静态变量永远不会是假的(false,0,null或空字符串) (49认同)
  • 略短且更清晰:( function(){var id = 0; function uniqueID(){return id ++;};})(); (3认同)
  • 关闭中的计数器比Firefox中的类快.http://jsperf.com/static-counter-in-class-vs-in-closure (3认同)

kho*_*ter 89

你通过IIFE(立即调用的函数表达式)来完成它:

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

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

incr(); // returns 1
incr(); // returns 2
Run Code Online (Sandbox Code Playgroud)

  • 我想说这是用JavaScript做的最惯用的方式.太糟糕了,由于其他方法对于来自其他语言的人来说可能更适合,因此不会得到太多的赞成. (19认同)
  • 我听说它们被称为匿名自调用函数,也称为“ASIF”(“好像”你实际上可以阅读它们。):) (2认同)

gpi*_*ino 40

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

function () {
  arguments.callee.myStaticVar = arguments.callee.myStaticVar || 1;
  arguments.callee.myStaticVar++;
  alert(arguments.callee.myStaticVar);
}
Run Code Online (Sandbox Code Playgroud)

  • `arguments.callee`已被弃用. (26认同)
  • 好一个.耻辱在严格模式下不起作用. (13认同)
  • 据我所知,这种方法比pascal MARTIN的方式有一个(只有一个?)优势:你可以在匿名函数上使用它.这方面的一个例子很棒 (3认同)

Mat*_*att 32

我已经看到了几个相似的答案,但我想提一下这篇文章最好描述,所以我想和你分享.

这里有一些代码,我已经修改了一些完整的例子,希望能给社区带来好处,因为它可以用作类的设计模板.

它还回答了你的问题:

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 + ' ...');
};
Run Code Online (Sandbox Code Playgroud)

在该示例中,您可以按如下方式访问静态属性/函数:

// access static properties/functions
Podcast.FILE_EXTENSION;                // 'mp3'
Podcast.download('Astronomy cast');    // 'Downloading Astronomy cast ...'
Run Code Online (Sandbox Code Playgroud)

对象特性/功能的简单地为:

// access object properties/functions
var podcast = new Podcast();
podcast.title = 'The Simpsons';
console.log(podcast.toString());       // Title: The Simpsons
console.log(podcast.immutableProp());  // 123
Run Code Online (Sandbox Code Playgroud)

请注意,在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) }
});
Run Code Online (Sandbox Code Playgroud)

它定义了属性d.yearvia getset函数 - 如果你没有指定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}
        };
Run Code Online (Sandbox Code Playgroud)

它定义了可读/可写属性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
Run Code Online (Sandbox Code Playgroud)

笔记:

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

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

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

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

'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"
Run Code Online (Sandbox Code Playgroud)

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

你可以这么说ab继承自Podcast.现在,如果你想在Podcast中添加一个适用于所有这些方法a并且b已经实现的方法,该怎么办?在这种情况下,使用.prototype如下:

Podcast.prototype.titleAndLink = function() {
    return this.title + " [" + this.link + "]";
};
Run Code Online (Sandbox Code Playgroud)

现在打电话ab再次:

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

您可以在此处找到有关原型的更多详细信息.如果你想做更多的继承,我建议调查一下.


强烈建议您阅读上面提到的文章系列,它们还包括以下主题:

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

请注意,JavaScript 的自动分号插入 "功能"(如6.中所述)经常导致代码中出现奇怪的问题.因此,我宁愿把它视为一个bug而不是一个特征.

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

什么是有趣的阅读,以及(也覆盖上述主题)是从这些文章MDN JavaScript的指南:

如果您想知道如何在JavaScript中模拟c#out参数(如in 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 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]"
Run Code Online (Sandbox Code Playgroud)
<div id="log"></div>
Run Code Online (Sandbox Code Playgroud)


笔记:

  • 关于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:&nbsp;</b>" + context.document.lastModified + "<br/>");        
        // ... more code ...
      }

      // public methods
      return {
        Main: _main
        // ... more public methods, properties ...
      };

    })(this);

    // use module
    myModule.Main("<b>Module demo</b>");
</script></body>
</html>
Run Code Online (Sandbox Code Playgroud)

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


小智 28

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();
Run Code Online (Sandbox Code Playgroud)


Max*_*ber 27

更新的答案:

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
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

您可以分配给属性,Foo因为在JavaScript函数中是对象.


小智 16

以下示例和说明来自Nicholas Zakas撰写的Professional 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()); // 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
Run Code Online (Sandbox Code Playgroud)

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

  • 这会将 Person 对象放在全局命名空间中。不是我推荐的解决方案。 (2认同)

Aut*_*ico 13

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

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

    console.log(MyClass.myStaticVariable);

    aMyClass = new MyClass();
    console.log(aMyClass.myStaticVariable, "is undefined");
Run Code Online (Sandbox Code Playgroud)

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


JΛY*_*ÐΞV 9

2021 年更新

2021年,您只需使用static关键字即可

自 2021 年 4 月起, TC39 将STATIC关键字移至Stage-4 Language Feature。花了很长时间才使staticJS 功能成为正式的 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
}

Run Code Online (Sandbox Code Playgroud)
上面的示例取自 TC39 存储库,静态字段


要了解有关此新 JS 语言功能实现的更多信息(请点击此处)。
要了解有关该功能本身的更多信息,以及查看演示用于静态字段的语法的示例(请单击此处)。


Hem*_*ant 8

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

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;
Run Code Online (Sandbox Code Playgroud)


COi*_*Oil 8

关于classECMAScript 2015介绍.其他答案并不完全清楚.

这里是展示了如何创建一个静态无功的例子staticVarClassName.varsynthax:

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
Run Code Online (Sandbox Code Playgroud)

要访问静态变量,我们使用该.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
Run Code Online (Sandbox Code Playgroud)


And*_*are 7

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

var thisIsGlobal = 1;

function foo() {
    var thisIsNot = 2;
}
Run Code Online (Sandbox Code Playgroud)

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

var foo = { bar : 1 }
Run Code Online (Sandbox Code Playgroud)

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


fun*_*oll 6

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

var nextCounter = (function () {
  var counter = 0;
  return function() {
    var temp = counter;
    counter += 1;
    return temp;
  };
})();
Run Code Online (Sandbox Code Playgroud)


Sна*_*ƒаӽ 6

您可以在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
Run Code Online (Sandbox Code Playgroud)

您可以使用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
Run Code Online (Sandbox Code Playgroud)


Jim*_*ard 6

除了其余的内容外,目前还有一份关于ECMA提案的草案(第2阶段的提案),在课堂上介绍了公共领域。(考虑了私人领域static

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

class CustomDate {
  // ...
  static epoch = new CustomDate(0);
}
Run Code Online (Sandbox Code Playgroud)

并等于其他人强调的以下内容:

class CustomDate {
  // ...
}
CustomDate.epoch = new CustomDate(0);
Run Code Online (Sandbox Code Playgroud)

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

您可以在中跟踪新建议proposal-static-class-features


当前,babel 可以使用您可以使用的转换类属性插件来支持此功能。此外,尽管仍在进行中,但仍在V8实施它


Joo*_*eat 5

如果要创建全局静态变量:

var my_id = 123;
Run Code Online (Sandbox Code Playgroud)

用以下替换变量:

Object.defineProperty(window, 'my_id', {
    get: function() {
            return 123;
        },
    configurable : false,
    enumerable : false
});
Run Code Online (Sandbox Code Playgroud)


Ker*_*rim 5

在JavaScript中,默认情况下变量是静态的.示例:

var x = 0;

function draw() {
    alert(x); //
    x+=1;
}

setInterval(draw, 1000);
Run Code Online (Sandbox Code Playgroud)

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

  • 那是另一种情况。你的例子是关于范围的。 (4认同)

Get*_*ree 5

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

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

function someFunc1(){
    if( !('staticVar' in someFunc1) )
        someFunc1.staticVar = 0 ;
    alert(++someFunc1.staticVar) ;
}

someFunc1() ; //prints 1
someFunc1() ; //prints 2
someFunc1() ; //prints 3
Run Code Online (Sandbox Code Playgroud)

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

var someFunc2 = (function(){
    var staticVar = 0 ;
    return function(){
        alert(++staticVar) ;
    }
})()

someFunc2() ; //prints 1
someFunc2() ; //prints 2
someFunc2() ; //prints 3
Run Code Online (Sandbox Code Playgroud)

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

var someFunc3 ;
with({staticVar:0})
    var someFunc3 = function(){
        alert(++staticVar) ;
    }

someFunc3() ; //prints 1
someFunc3() ; //prints 2
someFunc3() ; //prints 3
Run Code Online (Sandbox Code Playgroud)

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

{
    let staticVar = 0 ;
    function someFunc4(){
        alert(++staticVar) ;
    }
}

someFunc4() ; //prints 1
someFunc4() ; //prints 2
someFunc4() ; //prints 3
Run Code Online (Sandbox Code Playgroud)