Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Better way to access private members in Javascript

After reading a bit on Javascript's prototypical inheritance model, I change my style of constructing a class from

var Some_Class = function() {
    this.public_method = function() {
    };
    (function() {
        // constructor
    }).call(this)
}

to

var Some_Class = function() {
    (function() {
        // constructor
    }).call(this)
}
Some_Class.prototype.public_method = function() {
};

Although I understand that this is a good practice, but I am not allowed to access private methods from the public method anymore

var Some_Class = function() {
    var private_member = 'whatever';

    (function() {
        // constructor
    }).call(this)
}
Some_Class.prototype.public_method = function() {
    return private_member; // not possible
};

After reading through an article here (Closure-created constructor), then I came out with this

var Some_Class = function() {
    var private_member = 'whatever',

    private_method = function(_some_value) {
        // private method implementation
    };

    if(!arguments.callee.prototype.public_method) {
        arguments.callee.prototype.public_method = function() {
            private_method.call(this, private_method);
        };
    }

    (function() {
        // constructor
    }).call(this)
}

However, what are the drawbacks of doing this?! or is there a better way of doing this if I want to access private member in the public method?

like image 913
Jeffrey04 Avatar asked Jun 25 '09 03:06

Jeffrey04


2 Answers

My answer is a non-answer: there's no built-in private access in JavaScript but that's okay because YAGNI. Here's how I make private members in my code:

function Some_Class() {
    this._private_member = 'whatever';
}

Some_Class.prototype._private_method = function() {
};

That's good enough. It's not really worth it to jump through hoops when the only real purpose of private is to protect yourself from... yourself.

(I say this having spent many hours myself playing around with every permutation of closures and prototyping, just as you are, and finally saying "screw it, it's not worth it".)

like image 137
John Kugelman Avatar answered Sep 21 '22 15:09

John Kugelman


The use of function scope variables and closures to simulate private variables/functions is a well established idiom in the javascript community. If the variable is truly intended to be private, I see no drawback to this approach (although some claim that performant code on certain browsers/hosts has to pay attention to how many closures get created).

In your example, the private_method (and its environment) is shared across all objects - since your public_method closure is created only the first time the object is constructed (and bound to the constructor's prototype property that sets the created object's internal prototype chain) - so the private_method that is used is only the one that was created the first time.

Here is some sample code that will help illustrate what is going on:

  var global = 1;

  var Some_Class = function() {
    var private_method = 'whatever';
    var now = ++global;
    print("outer now: " + now );
    private_method = function(_some_value) {
        // private method implementation
        print("inner now: " + now);
    };

    if(!arguments.callee.prototype.public_method) {
        arguments.callee.prototype.public_method = function() {

            private_method.call(this, private_method);
        };
    }

    (function() {
        // constructor
    }).call(this)
}

new Some_Class().public_method(); // outer now: 2, inner now: 2
new Some_Class().public_method(); // outer now: 3, inner now: 2
new Some_Class().public_method(); // outer now: 4, inner now: 2

Are you sure that is what you want?

If your private_method does not need to refer to the enclosing object's state, then I see little benefit in doing things the way you are doing.

What I usually do (if i have to use 'new' to create my object) is the following:

function MyClass() {
  var private_var = 1; 
  function private_func()
  {

  }
  this.public_func = function() 
  {
     // do something
     private_func();
  }
  this.public_var = 10;
}

var myObj = new MyClass();

The downside to this approach is that each time you construct the object via 'new' you re-create all the closures. But unless my profiler tells me that this design choice needs to be optimized, i prefer its simplicity and clarity.

Also I don't see the benefit in your code of doing the following either:

  (function() { }).call(this);  // call the constructor

Why are you creating a separate scope in your constructor?

like image 23
Faisal Vali Avatar answered Sep 19 '22 15:09

Faisal Vali