Make a property that is read-only to the outside world, but my methods can still set

后端 未结 3 420
眼角桃花
眼角桃花 2020-12-28 08:49

In JavaScript (ES5+), I\'m trying to achieve the following scenario:

  1. An object (of which there will be many separate instances) each with a read-only property
3条回答
  •  慢半拍i
    慢半拍i (楼主)
    2020-12-28 09:24

    Honestly, I find that there's too many sacrifices to be made in order to enforce true privacy in JS (unless you are defining a module) so I prefer to rely on naming conventions only such as this._myPrivateVariable.

    This is a clear indicator to any developer that they shouldn't be accessing or modifying this member directly and it doesn't require to sacrifice the benefits of using prototypes.

    If you need your size member to be accessed as a property you will have no other choice but to define a getter on the prototype.

    function MyObj() {
        this._size = 0;
    }
    
    MyObj.prototype = {
        constructor: MyObj,
    
        incrementSize: function () {
            this._size++;
        },
    
        get size() { return this._size; }
    };
    
    var o = new MyObj();
    
    o.size; //0
    o.size = 10;
    o.size; //0
    o.incrementSize();
    o.size; //1
    

    Another approach I've seen is to use the module pattern in order to create a privates object map which will hold individual instances private variables. Upon instantiation, a read-only private key gets assigned on the instance and that key is then used to set or retrieve values from the privates object.

    var MyObj = (function () {
        var privates = {}, key = 0;
    
        function initPrivateScopeFor(o) {
           Object.defineProperty(o, '_privateKey', { value: key++ });
           privates[o._privateKey] = {};
        }
    
        function MyObj() {
            initPrivateScopeFor(this);
            privates[this._privateKey].size = 0;
        }
    
        MyObj.prototype = {
            constructor: MyObj,
    
            incrementSize: function () {  privates[this._privateKey].size++;  },
    
            get size() { return privates[this._privateKey].size; }
        };
    
        return MyObj;
    
    })();
    

    As you may have noticed, this pattern is interesting but the above implementation is flawed because private variables will never get garbage collected even if there's no reference left to the instance object holding the key.

    However, with ES6 WeakMaps this problem goes away and it even simplifies the design because we can use the object instance as the key instead of a number like we did above. If the instance gets garbage collected the weakmap will not prevent the garbage collection of the value referenced by that object.

提交回复
热议问题