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.

4
  • 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. Commented Nov 7, 2011 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). Commented Nov 7, 2011 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. Commented Nov 7, 2011 at 23:38
  • There are solutions based on the new features in ES6. For example see here: philipwalton.com/articles/… Commented Jun 4, 2014 at 2:17

8 Answers 8

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.

Sign up to request clarification or add additional context in comments.

Comments

9

You cannot do it in Javascript.

5 Comments

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.
This is true, trying to pretend otherwise is much more harmful than accepting this easy fact.
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).
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). gist.github.com/Benvie/3179490
You can create a class implementation that support protected and private members, for example: 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).
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
});

1 Comment

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: github.com/philipwalton/mozart
4

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

2 Comments

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

Comments

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"

Comments

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.

1 Comment

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.