2

I have this code here:

var Person = (function() {
    var name;

    var PersonConstructor = function(n) {
        name = n;
    };

    PersonConstructor.prototype.getName = function() {
        return name;
    };

    return PersonConstructor;

})();

var people = [];
var person1 = new Person("Foo");
var person2 = new Person("Bar");
alert(person1.getName()); // I want it to be Foo
people.push(person1);
people.push(person2);

I got the idea of emulating classes from here.. But of course, I neglected the fact that the private variable var name; is also a static variable. Since this is tripping my current efforts I would like to know if there is a way to keep the private behaviour in this example but avoid the static one?

  • 1
    You cannot have prototype functions refer to local variables in the constructor. I would stay away from ways to simulate visibility in JavaScript. That's just not part of the language and creates more problems than it helps. Use proper documentation, mark properties as `@private` and depending on the use case of your code, use something like Google's Closure compiler to only expose public properties by their actual name. – Felix Kling Apr 08 '12 at 12:06

3 Answers3

1

Use this.

var Person = (function() {
  var PersonConstructor = function(n) {
    this.name = n;
  };

  PersonConstructor.prototype.getName = function() {
    return this.name;
  };

  return PersonConstructor;    
})();

Unfortunately, this won't preserve the private state.

Alexander
  • 23,432
  • 11
  • 63
  • 73
  • This doesn't preserve "private" state of name? – Matt Esch Apr 08 '12 at 11:56
  • This does work but as stated it does not preserve the so-called private state, maybe I should stick to naming conventions then. –  Apr 08 '12 at 11:59
0

It's just a scope issue.

var Person = (function(){

    var PersonConstructor = function(n){ 
        // *************************************************************** 
        // PRIVATE VARIABLES AND FUNCTIONS 
        // ONLY PRIVELEGED METHODS MAY VIEW/EDIT/INVOKE 
        // *************************************************************** 

        var myName=n?n:"John Doe";

        // *************************************************************** 
        // PRIVILEGED METHODS 
        // MAY BE INVOKED PUBLICLY AND MAY ACCESS PRIVATE ITEMS 
        // MAY NOT BE CHANGED; MAY BE REPLACED WITH PUBLIC FLAVORS 
        // *************************************************************** 
        this.toString=this.getName=function(){ return myName } 
    } 
    return PersonConstructor;
})();

var person1 = new Person("foo");
var person2 = new Person("bar");

alert(person1.getName());
alert(person1.toString());
alert(person1.myName);

// alerts "foo", "foo", undefined

EDIT - Here is my original solution.

var Person = function(n){ 
    // *************************************************************** 
    // PRIVATE VARIABLES AND FUNCTIONS 
    // ONLY PRIVELEGED METHODS MAY VIEW/EDIT/INVOKE 
    // *************************************************************** 

    var myName=n?n:"John Doe";

    // *************************************************************** 
    // PRIVILEGED METHODS 
    // MAY BE INVOKED PUBLICLY AND MAY ACCESS PRIVATE ITEMS 
    // MAY NOT BE CHANGED; MAY BE REPLACED WITH PUBLIC FLAVORS 
    // *************************************************************** 
    this.toString=this.getName=function(){ return myName } 

} 

var person1 = new Person("foo");
var person2 = new Person("bar");

alert(person1.getName());
alert(person1.toString());
alert(person1.myName);

// alerts "foo", "foo", undefined
Chris Gessler
  • 22,727
  • 7
  • 57
  • 83
  • priviliged methods and public methods do not work together at all well. – Raynos Apr 08 '12 at 13:25
  • @Raynos - seems to work fine for me. Personally, I would do away with the prototype.getName but it's part of the OP's design, so I figured I'd leave it in there. – Chris Gessler Apr 08 '12 at 13:27
  • Your particular example would be better suited to a public read only property. In real world scenarios this doesn't work – Raynos Apr 08 '12 at 13:28
  • @Raynos - like I said, it was left in as an alternative to show that prototype.getName does work and that the private variable myName was just a scope issue. – Chris Gessler Apr 08 '12 at 13:31
  • You do see that your example code is just a really complex, ugly and inefficient read only public name property right? – Raynos Apr 08 '12 at 13:34
  • 1
    @Raynos - actually, it's quite simple vs. the accepted answer of using a state object (oh what, that was yours). I wanted to post a really, really simple version, but it didn't use the "PersonConstructor" and prototype stuff like the OP wanted. I've been doing OO JS for about 15 years now and know what I'm doing. Simple is better. – Chris Gessler Apr 08 '12 at 13:37
  • @Raynos - cleaned it up just for you, but now lacks the alternative samples that proved it was merely a scope issue. – Chris Gessler Apr 08 '12 at 13:49
-1

There is no "private" when working with prototypes.

It should be noted there is no value in private state, avoid it like the plague. Closures are ugly and expensive.

var o = {
    name: value
}

However if you insist on being delusional and want private state badly then

You can store state in a closure

function nameHolder(name) {
    return {
        get name() {
            return name
        },
        set name(n) {
            name = n
        }
    }
}

Note this is highly inefficient and has little benefit.

Alternatively you can store state in a weakmap

function privates() {
    var map = new WeakMap()

    return function (key) {
        var v = map.get(key)
        if (!v) {
            v = {}
            map.set(key, v)
        }
        return v
    }
}

var NameHolder = (function () {
    var state = privates()

    return {
        constructor: function (name) {
            state(this).name = name
        },
        getName: function () {
            return state(this).name
        }
    }
}())

WeakMap browser support is non-existant so emulate it using pd.Name

Raynos
  • 166,823
  • 56
  • 351
  • 396
  • Can you elaborate on this? So for every variable I may want to keep "private" (sorry don't know how to reffer to it) I have to create a holder function? EDIT: Okay, I saw your elaboration, I will try this though I have to try and get my head around it first. –  Apr 08 '12 at 12:02
  • 3
    I am surprised @Raynos was not more aggressive about the use of private state. It's a fair enough question, but ask yourself, do you really need private state? You could follow the convention of labelling all of your internal not-to-be-messed-with variables with a leading underscore. Could be easier to debug and it's more efficient. – Matt Esch Apr 08 '12 at 12:06
  • 1
    You claim closure is ugly and expensive. True that there is a cost to cross a scope boundary for reference lookups, but in well written code that cost can be minimized and in modern JIT interpreters it is often irrelevant outside of a loop. Closures are perhaps the most elegant and expressive feature in JavaScript. Closure is what makes a 2000 line equivalent Java program only 500 lines in JavaScript. – austincheney Apr 08 '12 at 13:25
  • Closures don't have anything to do with that. First class functions are powerful, excessive use of closures leads to bad code (in an OO style. Effective use of closures in functional style code works well). You should also benchmark it yourself, they still have a significant performance and complexity penalty – Raynos Apr 08 '12 at 13:30
  • Way too complex when the issue is simply the wrong scope. When the original code is scoped properly, everything works as expected. – Chris Gessler Apr 08 '12 at 13:45
  • Yes, as in my first closure example, it can be changed to use getter/setter methods instead as real getter/setters. The advantage of a weakmap is reduced memory usage – Raynos Apr 08 '12 at 14:24
  • "Closures are ugly and expensive." ... can you qualify this statement? Why are they ugly and expensive? – Ben Lesh May 02 '12 at 13:41
  • @blesh every closure you create is a new function. More functions = more memory. As for ugly, every closure you create increases the indentation level of your code by one and reduces the readability. Every closure you create increases the probability of a memory leak by one. There are a lot of issues with closures, they are powerful tools when they are needed, but when they are not needed they should be avoided. – Raynos May 02 '12 at 13:44
  • I see your point that the OP's use of closures was excessive. I think the word "expensive" may be misleading here, it leads one to think of system-crashing memory usage. While it would indeed use more memory for each closure used, it hardly seems like this would be a system killer, or even a deal breaker. – Ben Lesh May 02 '12 at 18:55
  • @blesh when you use closures with objects your memory usage goes upto to O(N) instead of O(1) – Raynos May 02 '12 at 18:57
  • @Raynos: Interesting. Can this be cleaned up by nulling out your object references? – Ben Lesh May 02 '12 at 19:33
  • @Raynos out of curiosity, are you the Raynos of Vows-Is? Seemed like a cool library. – Ben Lesh May 02 '12 at 20:26
  • @blesh vows-is clearly states at the top of it that it's deprecated – Raynos May 02 '12 at 20:35
  • @Raynos - Yes, I saw that, which is why I said it "seemed" like a cool library. It looked like good work. I recognized your name and I was complimenting you, that's all. – Ben Lesh May 02 '12 at 20:39
  • @blesh [chill in JS](http://chat.stackoverflow.com/rooms/17/javascript) for more casual banter o/ – Raynos May 02 '12 at 20:41