JavaScript中的静态变量

Static variables in JavaScript

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


如果您来自一个基于类的静态类型的面向对象语言(如Java、C++或C语言),那么我假设您正在尝试创建一个与"类型"相关联的变量或方法,而不是与实例相关联的变量或方法。

使用带有构造函数函数的"经典"方法的示例可能有助于您了解基本OO javascript的概念:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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将函数视为第一类对象,因此作为一个对象,可以为函数分配属性。


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

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

1
2
3
4
5
6
7
8
9
10
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中使用静态变量的技巧:

1
2
3
4
5
6
7
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.

这会得到相同的结果——除了这次返回的是递增的值,而不是显示的值。


通过IIFE(立即调用的函数表达式)执行此操作:

1
2
3
4
5
6
7
8
9
10
var incr = (function () {
    var i = 1;

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

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


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

1
2
3
4
5
function () {
  arguments.callee.myStaticVar = arguments.callee.myStaticVar || 1;
  arguments.callee.myStaticVar++;
  alert(arguments.callee.myStaticVar);
}


1
2
3
4
5
6
7
8
9
10
11
12
13
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();

我看到了一些类似的答案,但我想说的是,这篇文章描述的最好,所以我想和大家分享一下。好的。

这里有一些从中提取的代码,我已经修改了这些代码,以得到一个完整的示例,希望能给社区带来好处,因为它可以用作类的设计模板。好的。

它还回答了您的问题:好的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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 + ' ...');
};

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

1
2
3
// access static properties/functions
Podcast.FILE_EXTENSION;                // 'mp3'
Podcast.download('Astronomy cast');    // 'Downloading Astronomy cast ...'

对象属性/功能简单如下:好的。

1
2
3
4
5
// 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是在构造函数外部不可见的私有变量):好的。

1
2
3
4
5
6
// getters and setters
var 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。好的。

此语法也有效:好的。

1
2
3
4
5
// 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。好的。

用途:好的。

1
2
3
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

笔记:好的。

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

1
2
3
4
5
6
7
// instantiation helper
function Podcast() {
    if(false === (this instanceof Podcast)) {
        return new Podcast();
    }
// [... same as above ...]
};

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

1
2
var podcast = new Podcast(); // normal usage, still allowed
var podcast = Podcast();     // you can omit the new keyword because of the helper

"new"语句创建一个新对象并复制所有属性和方法,即好的。

1
2
3
4
5
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被声明之后,向播客添加一个适用于所有播客的方法,该怎么办?在这种情况下,使用.prototype如下:好的。

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

现在再给ab打电话:好的。

1
2
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打开开发人员工具并打开控制台选项卡,否则没有用于JavaScript的控制台)可能会发现以下代码段很有用。它允许您使用上述示例中使用的console.log(msg);。只需将它插入到Podcast函数之前。好的。

    为了您的方便,下面的代码是完整的单个代码片段:好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    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]"
    1
     

    好的。

    笔记:好的。

    • 关于javascript编程的一些好提示、提示和建议,您可以在这里(javascript最佳实践)和那里找到("var"与"let")。本文还建议使用隐式类型转换(强制)。好的。

    • 使用类并将其编译为JavaScript的一种方便方法是typescript。这里是一个游乐场,你可以在这里找到一些例子,向你展示它是如何工作的。即使您现在不使用typescript,您也可以查看,因为您可以将typescript与并排视图中的javascript结果进行比较。大多数例子都很简单,但也有一个光线跟踪器示例,您可以立即试用。我特别建议通过在组合框中选择"使用类"、"使用继承"和"使用泛型"示例来研究它们——这些是可以立即在JavaScript中使用的很好的模板。打字稿与斜角字一起使用。好的。

    • 为了在javascript中实现局部变量、函数等的封装,我建议使用如下的模式(jquery使用相同的技术):好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    <html>
    <head></head>
    <body>
        '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("last modified:&nbsp;" + context.document.lastModified +"<br/>");        
            // ... more code ...
          }

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

        })(this);

        // use module
        myModule.Main("Module demo");
    </body>
    </html>

    好的。

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


    更新答案:

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    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中可以这样做:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    //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中,函数是对象。


    下面的例子和解释来自NicholasZakas的《面向Web开发人员的专业JavaScript第二版》。这是我正在寻找的答案,所以我认为在这里添加它会有所帮助。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    (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()方法一样,可以访问私有变量名。使用此模式,名称变量将变为静态变量,并将在所有实例中使用。这意味着对一个实例调用setName()会影响所有其他实例。调用setName()或创建新的Person实例将name变量设置为新值。这会导致所有实例返回相同的值。


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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
        class MyClass {
          static get myStaticVariable() {
            return"some static variable";
          }
        }

        console.log(MyClass.myStaticVariable);

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

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


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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    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;

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

    下面是一个示例,演示如何使用ClassName创建静态变量staticVarvarsynthax:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    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属性,该属性返回对创建类的对象构造函数函数的引用。我们可以在两个创建的实例上调用它:

    1
    2
    3
    4
    5
    6
    7
    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中创建一个静态变量。这里,count是静态变量。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    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函数或任何实例为静态变量赋值:

    1
    2
    3
    4
    5
    6
    7
    // 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


    还有其他类似的答案,但没有一个能吸引我。我的结论是:

    1
    2
    3
    4
    5
    6
    7
    8
    var nextCounter = (function () {
      var counter = 0;
      return function() {
        var temp = counter;
        counter += 1;
        return temp;
      };
    })();

    在javascript中,默认情况下变量是静态的。例子:

    1
    2
    3
    4
    5
    6
    7
    8
    var x = 0;

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

    setInterval(draw, 1000);

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


    如果要生成全局静态变量:

    1
    var my_id = 123;

    将变量替换为以下内容:

    1
    2
    3
    4
    5
    6
    7
    Object.defineProperty(window, 'my_id', {
        get: function() {
                return 123;
            },
        configurable : false,
        enumerable : false
    });

    javascript中最接近静态变量的是全局变量-这只是一个在函数或对象文本范围之外声明的变量:

    1
    2
    3
    4
    5
    var thisIsGlobal = 1;

    function foo() {
        var thisIsNot = 2;
    }

    您可以做的另一件事是将全局变量存储在这样的对象文本中:

    1
    var foo = { bar : 1 }

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


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

    全局属性sessionstorage或localstorage允许在会话的生命周期内或无限长的时间内存储数据,直到明确清除为止。这允许在页面/应用程序的所有窗口、框架、选项卡面板、弹出窗口等之间共享数据,并且比一个代码段中的简单"静态/全局变量"功能强大得多。

    它避免了顶级全局变量(即window.myglobal)的作用域、生存期、语义、动态等的所有麻烦。不知道它有多高效,但对于以适度的速率访问的少量数据来说,这并不重要。

    以"sessionstorage.mydata=anything"轻松访问,检索方式类似。见"javascript:最终指南,第六版",David Flanagan,ISBN:978-0-596-80552-4,第20章,第20.1节。这很容易通过简单的搜索以PDF格式下载,或者在您的O'Reilly SafariBooks订阅中下载(它的重量相当于黄金)。


    要在此处浓缩所有类概念,请测试:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    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

    好吧,看看这些方面的最佳实践的另一种方法,就是看看coffeescript是如何翻译这些概念的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    #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);

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

    使用建议中的示例,建议的static语法如下:

    1
    2
    3
    4
    class CustomDate {
      // ...
      static epoch = new CustomDate(0);
    }

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

    1
    2
    3
    4
    class CustomDate {
      // ...
    }
    CustomDate.epoch = new CustomDate(0);

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

    你可以在proposal-static-class-features中跟踪新的提议。

    目前,babel支持这个带有转换类属性插件的特性,您可以使用它。此外,尽管仍在进行中,V8正在实施。


    函数的/类只允许对其对象范围使用单个构造函数。Function Hoisting, declarations & expressions

    • Functions created with the Function constructor do not create closures to their creation contexts; they always are created in the global scope.

      1
      2
      3
      4
      5
      6
      7
      8
        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)

    闭包-闭包的副本具有保留数据的功能。

    • 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.

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
        function closureFun( args ) {
              // Local variable that ends up within closure
              var num = args;
              num++;
              return function() { console.log(num); }
          }
          var closure1 = closureFun( 5 );
          var closure2 = closureFun( 777 );
          closure1(); // 5
          closure2(); // 777
          closure2(); // 778
          closure1(); // 6

    ES5函数类:使用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.

    通过使用``创建了一些方法,以便每个人都能轻松理解函数类。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    '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;
    }

    下面的代码片段是关于每个实例都有自己的实例成员副本和公共静态成员的测试。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    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

    Static method calls are made directly on the class and are not callable on instances of the class. But you can achieve the calls for static members from inside an instance.

    Using syntax:

    1
       this.constructor.staticfunctionName();
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    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模式简单。拥有一个简单的声明形式使类模式更容易使用,并鼓励互操作性。类支持基于原型的继承、超级调用、实例和静态方法以及构造函数。

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


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

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    function someFunc1(){
        if( !('staticVar' in someFunc1) )
            someFunc1.staticVar = 0 ;
        alert(++someFunc1.staticVar) ;
    }

    someFunc1() ; //prints 1
    someFunc1() ; //prints 2
    someFunc1() ; //prints 3

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    var someFunc2 = (function(){
        var staticVar = 0 ;
        return function(){
            alert(++staticVar) ;
        }
    })()

    someFunc2() ; //prints 1
    someFunc2() ; //prints 2
    someFunc2() ; //prints 3

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    var someFunc3 ;
    with({staticVar:0})
        var someFunc3 = function(){
            alert(++staticVar) ;
        }

    someFunc3() ; //prints 1
    someFunc3() ; //prints 2
    someFunc3() ; //prints 3

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    {
        let staticVar = 0 ;
        function someFunc4(){
            alert(++staticVar) ;
        }
    }

    someFunc4() ; //prints 1
    someFunc4() ; //prints 2
    someFunc4() ; //prints 3

    总结:

    ES6/es 2015中,引入了class关键字,并附带了static关键字。请记住,这是Javavscript所体现的原型继承模型的语法甜头。static关键字的工作方式如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    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

    }

    在chrome devtools中,我们可以很好地将其可视化:

    static variables javascript

    现在我们已经创建了一个static函数,我们可以通过ClassName.method()来执行它。


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

    1
    2
    3
    4
    5
    6
    7
    8
    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中没有静态变量。这种语言是基于原型的面向对象的,所以没有类,而是从原型中对象"复制"自己。

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

    1
    2
    3
    function circle(){
    }
    circle.prototype.pi=3.14159


    窗口级变量在某种意义上类似于静态变量,您可以使用直接引用,这些变量可用于应用程序的所有部分。


    使用使用jquery的MVC网站,我希望确保某些事件处理程序中的Ajax操作只能在上一个请求完成后执行。我使用"static"jqxhr对象变量来实现这一点。

    给定以下按钮:

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

    对于我的点击处理程序,我通常使用这样的iLife:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    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);

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    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);

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


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

    如果我想存储函数的局部值,我会使用"local.x"、"local.y"、"local.tempdata"等函数…

    如果我想存储函数静态值,我会使用"static.o"、"static.info"、"static.count"等函数…

    [update2]:同样的方法,但使用的是iife方法!

    [update1]:"static"和"local"函数对象是通过预编辑脚本自动创建的!


    所以我看到的其他答案是,它们没有解决面向对象编程中静态属性的基本体系结构需求。

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

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

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

    1
    2
    3
    4
    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,更改将级联到立即继承它的子对象。

    但是,有一些"gotchas"可能会严重破坏该属性的"静态"性质,或者只留下安全漏洞…

    首先,确保通过将构造函数封闭在另一个函数(如jquery ready方法)中来隐藏全局命名空间中的构造函数。

    1
    2
    3
    4
    5
    6
    7
    8
     $(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
    });

    第二个也是最后一个,即使这样做,属性仍然可以从自己脚本的任何其他部分进行编辑,因此可能是代码中的bug将属性写入其中一个子对象,并将其与父原型分离,因此如果更改父属性,它将不再层叠和更改stati。子对象的C属性。看看这个J小提琴。在不同的场景中,我们可以使用Object.freeze(obj)来停止对子对象的任何更改,也可以在构造函数中设置setter和getter方法并访问闭包,这两者都具有相关的复杂性。

    在我看来,在基于类的"静态属性"概念和这个JavaScript实现之间并没有完美的相似之处。因此,我认为从长远来看,使用一种更适合JavaScript的不同代码模式可能会更好。例如一个中央数据存储或缓存,甚至一个专用的助手对象来保存所有必要的静态变量。


    我没有在任何答案中看到这个想法,所以只是把它添加到列表中。如果是复制品,请告诉我,我会删除它并对另一个进行升级投票。

    我在自己的网站上创建了一种超级全球化。因为我有几个JS文件在每次页面加载时都会被加载,还有几十个其他JS文件只在某些页面上加载,所以我将所有"global"函数放在一个全局变量中。

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

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

    然后我将介绍几个全局助手函数

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

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    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;
    }

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


    对于私有静态变量,我发现了以下方法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    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以使用函数对象属性,那么理论上,在javascript中可以有一个静态变量。

    例如:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    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 1
    var dog = new Animal();
    console.log(cat.totalAnimalCount); //Will produce 2 and so on.


    在JavaScript中,所有内容要么是基元类型,要么是对象。函数是对象&mdash;(键值对)。

    创建函数时,将创建两个对象。一个对象表示函数本身,另一个对象表示函数的原型。

    从这个意义上说,函数是一个具有以下属性的对象:

    1
    2
    3
    function name,
    arguments length
    and the functional prototype.

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

    下面是一个片段,它使用newjavascript关键字创建该端,甚至实例化两个实例。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    function C () { // function
      var 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 1
    var 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使用相同的静态属性。


    我经常使用静态函数变量,但遗憾的是JS没有内置的机制。我经常看到代码,其中变量和函数是在外部范围中定义的,即使它们只是在一个函数中使用。这很难看,容易出错,只是在自找麻烦…

    我想出了以下方法:

    1
    2
    3
    4
    5
    6
    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()result。

    然后你可以做:

    1
    2
    3
    4
    5
    function f() {
      const _s = f.statics(() => ({ v1=3, v2=somefunc() });

      if (_s.v1==3) { ++_s.v1; _s.v2(_s.v1); }
    }

    与另一个正确答案的IIFE相比,这有一个缺点,即在每个函数调用上添加一个赋值和一个if,并向函数添加一个"statics"成员,但是有几个优点:参数在顶部,而不是在内部函数中,使用内部函数代码中的"static"进行解释。ICIT的前缀是"_s.",而且查看和理解起来更简单。


    "班级"制度

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    var Rect = (function(){
        'use strict';
         return {
            instance: function(spec){
                'use strict';
                spec = spec || {};

                /* Private attributes and methods */
                var x = (spec.x === undefined) ? 0 : spec.x,
                y = (spec.x === undefined) ? 0 : spec.x,
                width = (spec.width === undefined) ? 1 : spec.width,
                height = (spec.height === undefined) ? 1 : spec.height;

                /* Public attributes and methods */
                var that = { isSolid: (spec.solid === undefined) ? false : spec.solid };

                that.getX = function(){ return x; };
                that.setX = function(value) { x = value; };

                that.getY = function(){ return y; };
                that.setY = function(value) { y = value; };

                that.getWidth = function(){ return width; };
                that.setWidth = function(value) { width = value; };

                that.getHeight = function(){ return height; };
                that.setHeight = function(value) { height = value; };

                return that;
            },

            copy: function(obj){
                return Rect.instance({ x: obj.getX(), y: obj.getY(), width: obj.getWidth, height: obj.getHeight(), solid: obj.isSolid });
            }
        }
    })();


    你可以这样想。在中放置一个标签

    并将其设置为visibility: hide

    当然,您可以使用jquery管理前一个标记中的文本。实际上,这个标签成为静态变量。


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    {
       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());

    这只是我在某个地方学到的静态变量的另一种方法。


    当我看到这个的时候,我记得javascript的闭包。我是这样做的……

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
            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