Static variables in JavaScript

后端 未结 30 2258
别那么骄傲
别那么骄傲 2020-11-22 01:55

How can I create static variables in Javascript?

30条回答
  •  北荒
    北荒 (楼主)
    2020-11-22 02:32

    I've seen a couple of similar answers, but I'd like to mention that this post describes it best, so I'd like to share it with you.

    Here's some code taken from it, which I have modified to get a complete example which hopefully gives benefit to the community because it can be used as a design template for classes.

    It also answers your question:

    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 + ' ...');
    };
    

    Given that example, you can access the static properties/function as follows:

    // access static properties/functions
    console.log(Podcast.FILE_EXTENSION);   // 'mp3'
    Podcast.download('Astronomy cast');    // 'Downloading Astronomy cast ...'
    

    And the object properties/functions simply as:

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

    Note that in podcast.immutableProp(), we have a closure: The reference to _somePrivateVariable is kept inside the function.

    You can even define getters and setters. Take a look at this code snippet (where d is the object's prototype for which you want to declare a property, y is a private variable not visible outside of the constructor):

    // getters and setters
    var d = Date.prototype;
    Object.defineProperty(d, "year", {
        get: function() {return this.getFullYear() },
        set: function(y) { this.setFullYear(y) }
    });
    

    It defines the property d.year via get and set functions - if you don't specify set, then the property is read-only and cannot be modified (be aware you will not get an error if you try to set it, but it has no effect). Each property has the attributes writable, configurable (allow to change after declaration) and enumerable (allow to use it as enumerator), which are per default false. You can set them via defineProperty in the 3rd parameter, e.g. enumerable: true.

    What is also valid is this syntax:

    // getters and setters - alternative syntax
    var obj = { a: 7, 
                get b() {return this.a + 1;}, 
                set c(x) {this.a = x / 2}
            };
    

    which defines a readable/writable property a, a readonly property b and a write-only property c, through which property a can be accessed.

    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
    

    Notes:

    To avoid unexpected behaviour in case you've forgotten the new keyword, I suggest that you add the following to the function Podcast:

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

    Now both of the following instantiations will work as expected:

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

    The 'new' statement creates a new object and copies all properties and methods, i.e.

    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"
    

    Note also, that in some situations it can be useful to use the return statement in the constructor function Podcast to return a custom object protecting functions the class internally relies on but which need to be exposed. This is explained further in chapter 2 (Objects) of the article series.

    You can say that a and b inherit from Podcast. Now, what if you want to add a method to Podcast that applies to all of them after a and b have been instanciated? In this case, use the .prototype as follows:

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

    Now call a and b again:

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

    You can find more details about prototypes here. If you want to do more inheritance, I suggest looking into this.


    The article series I've mentioned above are highly recommended to read, they include also the following topics:

    1. Functions
    2. Objects
    3. Prototypes
    4. Enforcing New on Constructor Functions
    5. Hoisting
    6. Automatic Semicolon Insertion
    7. Static Properties and Methods

    Note that the automatic semicolon insertion "feature" of JavaScript (as mentioned in 6.) is very often responsible for causing strange issues in your code. Hence, I would rather regard it as a bug than as a feature.

    If you want to read more, here is a quite interesting MSDN article about these topics, some of them described there provide even more details.

    What is interesting to read as well (also covering the topics mentioned above) are those articles from the MDN JavaScript Guide:

    • A re-introduction to JavaScript
    • Working with Objects

    If you want to know how to emulate c# out parameters (like in DateTime.TryParse(str, out result)) in JavaScript, you can find sample code here.


    Those of you who are working with IE (which has no console for JavaScript unless you open the developer tools using F12 and open the console tab) might find the following snippet useful. It allows you to use console.log(msg); as used in the examples above. Just insert it before the Podcast function.

    For your convenience, here's the code above in one complete single code snippet:

    let console = { log: function(msg) {  
      let canvas = document.getElementById("log"), br = canvas.innerHTML==="" ? "" : "
    "; 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]"


    Notes:

    • Some good tips, hints and recommendations about JavaScript programming in general you can find here (JavaScript best practices) and there ('var' versus 'let'). Also recommended is this article about implicit typecasts (coercion).

    • A convenient way to use classes and compile them into JavaScript is TypeScript. Here is a playground where you can find some examples showing you how it works. Even if you're not using TypeScript at the moment, you can have a look because you can compare TypeScript with the JavaScript result on a side-by-side view. Most examples are simple, but there is also a Raytracer example which you can try out instantly. I recommend especially looking into the "Using Classes", "Using Inheritance" and "Using Generics" examples by selecting them in the combobox - these are nice templates you can instantly use in JavaScript. Typescript is used with Angular.

    • To achieve encapsulation of local variables, functions etc in JavaScript, I suggest to use a pattern like the following (JQuery uses the same technique):

    
    
    
    

    Of course, you can - and should - put the script code in a separate *.js file; this is just written inline to keep the example short.

    Self-invocing functions (also known as IIFE = Immediately Invoked Function Expression) are described in more detail here.

提交回复
热议问题