javascript static - JavaScript中的靜態變量




15 Answers

如果您來自基於類的靜態類型的面向對象語言(如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對staticProperty中定義(這是一個函數),並且與其創建的實例無關,JavaScript將函數視為一級對象 ,因此作為對象,您可以將屬性分配給函數。

variable es6

我如何在Javascript中創建靜態變量?




你通過一個IIFE(立即調用的函數表達式)來完成它:

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

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

incr(); // returns 1
incr(); // returns 2



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



更新回答:

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中,函數是對象。




如果您正在使用新的類語法,那麼現在可以執行以下操作:

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

    console.log(MyClass.myStaticVariable);

    aMyClass = new MyClass();
    console.log(aMyClass.myStaticVariable, "is undefined");

這有效地在JavaScript中創建了一個靜態變量。




還有其他類似的答案,但沒有一個對我很有吸引力。 這就是我最終的結果:

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



如果你想創建一個全局靜態變量:

var my_id = 123;

用下面的代碼替換變量:

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



JavaScript中最接近靜態變量的是一個全局變量 - 這僅僅是一個在函數或對象文本範圍外聲明的變量:

var thisIsGlobal = 1;

function foo() {
    var thisIsNot = 2;
}

你可以做的另一件事是將全局變量存儲在像這樣的對象文本中:

var foo = { bar : 1 }

然後像這樣訪問變體: foo.bar




為了濃縮這裡的所有類概念,測試一下:

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



在JavaScript中,沒有術語或關鍵字static,但我們可以將這些數據直接放入函數對像中(就像其他任何對像一樣)。

function f() {
    f.count = ++f.count || 1 // f.count is undefined at first
    alert("Call No " + f.count)
}

f(); // Call No 1

f(); // Call No 2



In addition to the rest, there's currently a draft ( stage-2 proposal ) on ECMA Proposals that introduces static public fields in classes. ( private fields were considered )

Using the example from the proposal, the proposed static syntax will look like this:

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

and be equivalent to the following which others have highlighted:

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

You can then access it via CustomDate.epoch .

You can keep track of the new proposal in proposal-static-class-features .

Currently, babel supports this feature with the transform class properties plugin which you can use. Additionally, though still in progress, V8 is implementing it .




Window level vars are sorta like statics in the sense that you can use direct reference and these are available to all parts of your app




Working with MVC websites that use jQuery, I like to make sure AJAX actions within certain event handlers can only be executed once the previous request has completed. I use a "static" jqXHR object variable to achieve this.

Given the following button:

<button type="button" onclick="ajaxAction(this, { url: '/SomeController/SomeAction' })">Action!</button>

I generally use an IIFE like this for my click handler:

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



So what I see with the other answers is that they don't address the fundamental architectural requirement of a static attribute in object oriented programming.

Object oriented programming actually has two different styles one is 'class based' (C++, C#, Java etc), the other is 'prototypal' (Javascript). In class based languages a 'static attribute' is supposed to be associated with the class and not the instantiated objects. This concept actually works much more intuitively in a prototypal languages like Javascript because you just assign the attribute as a value of the parent prototype like so.

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

And access it from every one of the objects that is instantiated from this constructor like so...

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

Now if you go ahead and change the MyObject.prototype.staticAttribute the change will cascade down to the child objects that immediately inherit it.

However there are a few 'gotchas' that could significantly undermine the 'static' nature of this attribute, or just leave security vulnerability...

First make sure to hide the constructor from the Global namespace by enclosing it inside another function like the jQuery ready method

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

Second and lastly, even if you do this, the attribute is still editable from any of the other parts of your own script, so it could be the case that a bug in your code writes over the attribute on one of the child objects and detaches it from the parent prototype, so if you change the parent attribute it will no longer cascade and change the static attribute for the child object. See this jsfiddle. In different scenarios we could either Object.freeze(obj) to stop any changes to the child object, or we could set up a setter and getter method in the constructor and access a closure, both of these have associated complexities.

It seems to me that there is not a perfect analogue between the class-based idea of a 'static attribute' and this Javascript implementation. So I think it might be better in the long run to use a different code pattern that is more Javascript friendly. Such as a central datastore or cache or even a dedicated helper object to hold all the necessary static variables.




For private static variables, I found this way:

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



Related