13

Is there a JavaScript pattern which mimics "Protected" object properties like what you see in languages like C++ ??

Basically, I'd like to create an Object A which has a number of "protected" object properties which can be accessed ONLY from methods which are defined from the prototype of Object A. i.e. - NOT accessible publicly from non-prototyped methods of A.

For instance, ideally would be like so:

function A(){
    var prop1 = 1;      
}

A.prototype.myFunc = function(){
    var newVar = this.prop1;   //newVar now is equivalent to 1
}

var instanceOfA = new A();
var newVar2 = instanceOfA.prop1;  //error given as prop1 is "protected"; hence undefined in this case

BTW - I do not want the pattern of privileged member functions accessing private properties since the member function is still public.

Robert Harvey
  • 178,213
  • 47
  • 333
  • 501
Drake Amara
  • 3,072
  • 3
  • 19
  • 18
  • 7
    My advice, as so often, is to not force visibility into JavaScript. *Some* things are possible with closure etc, but the language is not designed for that. It will make your code more complex. Instead, document your methods properly as private or public and if other developers do not follow your specification, it's their problem. – Felix Kling Nov 07 '11 at 23:37
  • Btw, prototype objects can be augmented (they are not sealed) - nothing prevents an intruder to add new methods to the prototype object. So, having a property accessible only via prototype methods would not be safe anyway (even if it were possible). – Šime Vidas Nov 07 '11 at 23:37
  • JavaScript isn't Class-oriented, it's Object-oriented--not in the sense of object-oriented that refers to class instances, but in the sense of... just objects. There's no features like your usual class member keywords in Java or C++, you need to instead design your implementation based on the object and event-driven nature of the language. – MischaNix Nov 07 '11 at 23:38
  • There are solutions based on the new features in ES6. For example see here: http://philipwalton.com/articles/implementing-private-and-protected-members-in-javascript/ – Andrew Savinykh Jun 04 '14 at 02:17

8 Answers8

12

There is no object property that can only be accessed from prototyped methods of A and not from non-prototyped methods of A. The language doesn't have that type of feature and I'm not aware of any work-around/hack to implement it.

Using Doug Crockford's methods, you can create member properties that can only be accessed from predefined non-prototyped methods (those defined in the constructor). So, if you're trying to limit access only to a predefined set of methods, this will accomplish that. Other than that, I think you're out of luck.

If you want other ideas, you'd probably get more help if you describe more about what you're actually trying to accomplish in your code rather than just how to emulate a feature in another language. Javascript is so much different than C++ that it's better to start from the needs of the problem rather than try to find an analogy to some C++ feature.

jfriend00
  • 683,504
  • 96
  • 985
  • 979
9

You cannot do it in Javascript.

Thomas Eding
  • 35,312
  • 13
  • 75
  • 106
  • Can you explain why I got a -1? What I wrote is entirely true in JS. You cannot create protected properties in JS. There's nothing more to it. – Thomas Eding Nov 08 '11 at 21:50
  • 4
    This is true, trying to pretend otherwise is much more harmful than accepting this easy fact. – Esailija Jul 29 '12 at 22:53
  • 1
    Citation not needed. It's a fact. You will not find any mention of protected variables in the Javascript specification (meaning it is impossible to cite it). – Thomas Eding Oct 21 '13 at 20:58
  • 1
    It is very much possible to create protected members in Javascript by using WeakMap. It's also possible to emulate WeakMap in ES5 (back in 2011 when you made this comment). https://gist.github.com/Benvie/3179490 – user2867288 Dec 08 '16 at 18:29
  • You can create a class implementation that support protected and private members, for example: https://github.com/philipwalton/mozart. Using a tool like this, you can define classes that have protected and private properties across the class definition, and those properties are not accessible on the outside. So, it is possible! (But perhaps not as ideal as in Java or TypeScript). – trusktr May 15 '17 at 22:57
5

I found a way for creating protected members. Therefor I call the base constructor and return an object with the protected members at the same time:

var protected = BaseClass.call(this); 

Here an example:

function SignedIntegerArray(size)
{
    var public = this;
    var protected = {};

    // private property:
    var _maxSize = 10000;
    // protected property:
    protected.array = [];
    // public property:
    public.Length = size;

    if(!isInteger(size) || size < 0 || size > _maxSize) { throw "argument exception"; }
    for(var index = 0; index != size; index++) { protected.array[index] = 0; }

    // private method:
    function isInteger(i) { return i == i + 0 && i == ~~i; }
    // protected method:
    protected.checkIndex = function(index) { return index >= 0 && index < size; }
    // public methods:
    public.SetValue = function(index, value) { if(protected.checkIndex(index) && isInteger(value)) { protected.array[index] = value; } };
    public.GetValue = function(index) { if(protected.checkIndex(index)) { return protected.array[index]; } else { throw "index out of range exception"; }}

    return protected;
}

function FloatArray(size, range)
{
    var public = this;
    var protected = SignedIntegerArray.call(this, size); // call the base constructor and get the protected members 

    // new private method, "isInteger" is hidden...
    function isFloat(argument) { return argument != ~~argument; }
    // ...but "checkIndex" is accessible
    public.SetValue = function(index, value) { if(protected.checkIndex(index) && isFloat(value) && value >= public.MinValue && value <= public.MaxValue) { protected.array[index] = value; } };

    // new public properties:
    public.MinValue = -range;
    public.MaxValue = range;

    return protected; // for sub-classes
}

function newObject(className, args) { return new function() { className.apply(this, args)}} // you need to use function.call or function.apply to initialize an object. otherwise the protected-object is empty.
window.addEventListener("load", function()
{
    var o = newObject(FloatArray, [4, 50.0]);
    o.SetValue(3, 2.1);
    console.log(o.GetValue(3));
    console.log(o.Length); // property from the base-class
});
Martin Wantke
  • 4,287
  • 33
  • 21
  • The downside to this approach is that you waste memory for each isntance, because each instance has duplicated functions rather than re-using methods on prototypes. So for 100 instances you have 100 versions of the functions defined in the constructor, whereas with prototype methods you'd just have only one function instance. See this for a method that doesn't waste memory: https://github.com/philipwalton/mozart – trusktr May 15 '17 at 23:00
4

This is probably what you're looking for: http://javascript.crockford.com/private.html

Anony372
  • 494
  • 2
  • 15
  • I agree. That Doug Crockford article is `the` definitive description of the options for member variable privacy. This is what is available. They're all somewhat hacks since every member variable officially supported by the language is public, but you can obtain privacy in various ways by using closures. – jfriend00 Nov 07 '11 at 23:40
  • 3
    I would also like to point out that while you can do what Crockford has in that article, does not mean you should do it. Program in language X like you are programming for language X, not language Y. That said, there are some legitimate uses for (!AHEM!) private variables (I think my mouth just bled by saying that) in JS, but I won't go into it, as those uses don't really have anything to do with class design so to speak. – Thomas Eding Nov 07 '11 at 23:50
2
function ClassA(init)
{
    var protected = {};
    protected.prop = init * 10;
    if(this.constructor != ClassA) { return protected; }
}

function ClassB()
{
    var protected = ClassA.call(this, 5); //console.log(protected.prop);
}

//var a = new ClassA(123);
//var b = new ClassB();
Martin Wantke
  • 4,287
  • 33
  • 21
1

I was interested to find a way to answer your question, and here's what I was able to do.

You'll need this helper:

var ProtectedHandler = (function () {
    /// <Sumarry>
    /// Tool to handle the protected members of each inheritance.
    /// </Summary>
    /// <param name="current">Current protected variable.</param>
    /// <param name="args">The arguments variable of the object.</param>
    /// <param name="callback">The function to initialise the variable in the 'object'.</param>
    /// <param name="isParent">Is this the ultimate base object.</param>
    function ProtectedHandler(current, args, callback, isParent) {
        this.child = getChild(args);
        if (callback)
            this.callback = callback;

        if (isParent)
            this.overrideChild(current);
    }

    // Get the ProtectedHandler from the arguments
    var getChild = function (args) {
        var child = null;
        if (args.length > 0 && (child = args[args.length - 1]) && child.constructor === ProtectedHandler)
            return child;
    };

    // Chain Initialise the protected variable of the object and its inheritances.
    ProtectedHandler.prototype.overrideChild = function (newValue) {
        if (this.callback != null) {
            this.callback(newValue);
        }
        if (this.child != null) {
            this.child.overrideChild(newValue);
        }
    };

    // Static function to create a new instance of the protectedHandler object.
    ProtectedHandler.handle = function (protected, arguments, callback, isParent) {
        return new ProtectedHandler(protected, arguments, callback, isParent);
    };

    return ProtectedHandler;
})();

This helper will allow you to handle multiple inheritances. The trick is to copy the protected variable from the base object to your new object (child).

To prove you it's working, here's an example:

// That's the default extends function from typescript (ref: http://www.typescriptlang.org/)
var __extends = this.__extends || function (d, b) {
    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
    function __() { this.constructor = d; }
    __.prototype = b.prototype;
    d.prototype = new __();
};

var BaseClass = (function () {        
    function BaseClass() {
        // Members
        var private = {},
            protected = {},
            public = this;

        // Constructor
        ProtectedHandler.handle(protected, arguments, function () {
            protected.type = "BaseClass";
        }, true);

        // Methods
        protected.saySomething = function () {
            return "Hello World";
        };

        public.getType = function () {
            return protected.type;
        };
    }

    return BaseClass;
})();



var Person = (function (_super) {
    __extends(Person, _super);

    function Person(name) {
        // Members
        var private = {},
            protected = {},
            public;

        // Constructor
        _super.call(public = this, ProtectedHandler.handle(protected, arguments, function (p) {
            protected = p; //This is required to copy the object from its base object.
            protected.name = name;
            protected.type = "Person";
        }));

        //Method
        public.getName = function () {
            return protected.name;
        };

        public.saySomething = function () {
            return protected.saySomething();
        };
    }
    return Person;
})(BaseClass);


var Child = (function (_super) {
    __extends(Child, _super);

    function Child(name) {
        // Members
        var private = {},
            protected = {},
            public;

        // Constructor
        _super.call(public = this, name, ProtectedHandler.handle(protected, arguments, function (p) {
            protected = p; //This is required to copy the object from its base object.
            protected.type = "Child";
        }));

        //Method
        public.setName = function (value) {
            return protected.name = value;
        };
    }
    return Child;
})(Person);

And here's the tests:

var testBase = new BaseClass();
testBase.getType(); //"BaseClass"
testBase.saySomething; //undefined

var testPerson = new Person("Nic");
testPerson.getType(); //"Person"
testPerson.saySomething(); //"Hello World"
testPerson.name; //undefined
testPerson.getName() //"Nic"
testPerson.setName; //undefined

var testChild = new Child("Bob");
testChild.getType(); //"Child"
testChild.saySomething(); //"Hello World"
testChild.name; //undefined
testChild.getName(); //"Bob"
testChild.setName("George");
testChild.getName(); //"George"
Finickyflame
  • 853
  • 10
  • 12
1

There is a pattern that I have come to like that does not work the same way as protected access does in most languages, but provides a similar benefit.

Basically, use a builder method to create a closure for properties, and then have that method create a "full" object with liberal access as well as an "exposed" object with more limited access. Place the exposed object into a property of the full object, and return that full object to the caller.

The caller can then make use of the full object (and pass that to other appropriate collaborators), but provide only the exposed object to collaborators that should have the more restricted access.

A contrived example…

// Ring employs a typical private/public pattern while
// RingEntry employs a private/exposed/full access pattern.

function buildRing( size ) {
  var i
    , head = buildRingEntry( 0 )
    , newEntry;
  ;
  head.setNext( head );
  for( i = size - 1; i ; i-- ) {
    newEntry = buildRingEntry( i );
    newEntry.setNext( head.getNext() );
    head.setNext( newEntry );
  }
  function getHead() { return head.exposed; }
  return {
      getHead : getHead
  }
}

function buildRingEntry( index ) {
  var next
    , exposed
  ;
  function getIndex() { return index; }
  function setNext( newNext ) { next = newNext; }
  function getNextFullEntry() { return next; }
  function getNextExposedEntry() { return next.exposed; }
  exposed = {
      getIndex : getIndex
    , getNext  : getNextExposedEntry
  };
  return {
      getIndex : getIndex
    , setNext  : setNext
    , getNext  : getNextFullEntry
    , exposed  : exposed
  };
}

If we use that to build a ring of 4 entries ring = buildRing(4);, then ring.getHead().getIndex() gives us 0, ring.getHead().getNext().getIndex() gives us 1, ring.getHead().getNext().getNext().getIndex() gives us 2, etc.

If we try to execute ring.getHead().setNext({}) or ring.getHead().getNext().setNext({}), however, we get an error because setNext is not a property of an exposed entry object.

Caveat:

Since this is in the family of patterns that build the methods again in a new closure for each new object, it is not suitable for situations in which a very high volume of instantiation may be needed.

Steve Jorgensen
  • 11,725
  • 1
  • 33
  • 43
  • Note that this pattern can also be used with a constructor function for the full object. Simply create & assign the exposed property of `this` from within the constructor body. – Steve Jorgensen Jan 21 '16 at 08:59
0

Take a look at workaround proposed by Maks on his website: Emulating protected members in JavaScript

It emulates protected access level to methods and properties of an object.

NullUserException
  • 83,810
  • 28
  • 209
  • 234
Roman
  • 1