3

I have an object containing both public and private variables. The public variables are assigned to the private variables (I think), however, whenever I modify the private variables with a function, the public variables don't update.

var foo = (function() {
    //Private vars
    var a = 1;

    return {
        //Public vars/methods
        a: a,
        changeVar: function () {
            a = 2;
        }
    }
})();
alert(foo.a);  //result: 1
foo.changeVar();
alert(foo.a);  //result: 1, I want it to be 2 though

Now I know that if I change the line in changeVar to this.a = 2; it works but then it doesn't update the private variable. I want to update both the private and public variables at the same time. Is this possible?

JsFiddle showing problem

Michael
  • 5,994
  • 7
  • 44
  • 56
  • how about this approach? http://marcelorjava.wordpress.com/2014/06/07/object-oriented-programming-with-java-and-javascript/ – the_marcelo_r Jun 07 '14 at 11:27

4 Answers4

8

When you set the a key in the object you're returning, that's making a copy of the 'private' a variable.

You can either use a getter function:

return {
    //Public vars/methods
    a: function() { return a; },
    changeVar: function () {
        a = 2;
    }
};

Or you can use Javascript's built-in accessor functionality:

obj = {
    //Public vars/methods
    changeVar: function () {
        a = 2;
    }
};
Object.defineProperty(obj, "a", { get: function() { return a; } });
return obj;
  • why is `a = 1; b = {a:a}; b.a++` won't effect a, but `a=[]; b= {a:a}; b.a.push(1)` effects a? – lostyzd Oct 08 '11 at 04:43
  • 1
    @lostyzd http://stackoverflow.com/questions/6605640/javascript-by-reference-vs-by-value –  Oct 08 '11 at 04:44
2

Yes, if you're using a newer browser:

var foo = (function() {
    var a = 1;
    return {
        get a() { return a; },
        changeVar: function () {
            a = 2;
        }
    }
})();

See a demo on JSFiddle.

There's also a more compatible method, but it requires changing the code that uses it:

var foo = (function() {
    var a = 1;
    return {
        getA: function() { return a; },
        changeVar: function () {
            a = 2;
        }
    }
})();
alert(foo.getA()); // rather than foo.a

If neither of these methods work for you, you'll have to either always assign both or always reference one (which must be the public one if you intend it to be public.

icktoofay
  • 126,289
  • 21
  • 250
  • 231
1

I normally use this pattern, that I haven't seen many do. I do this to avoid having to order my methods in any special way. If all is public, then one normally has to ensure that the methods called, are declared before the method call

var person = new Person("Mohamed", "Seifeddine");
person.getFullname();
person.getFirstname();
person.getLastname();           

function Person(firstname, lastname) {
    var firstname, lastname;

    (function constructor(){
        setFirstname(firstname);
        setLastname(lastname)
    })();

    this.getFullname = getFullname;   // Makes getFullName() public 
    function getFullname() {
        // Will allow you to order method in whatever order you want. 
        // If we where to have it as just this.getFullname = function () {...} and same for firstname 
        // as it is normally done, then this.getFirstname would have to be placed before this method. 
        // A common pain in the ass, that you cannot order methods as you want!    
        return getFirstname() + ", " + getLastname();   
    }               

    this.getFirstname = getFirstname;
    function getFirstname() {
        return firstname;
    }

    function setFirstname(name){
        firstname = name;
    }

    this.getLastname = getLastname;
    function getLastname() {
        return lastname;
    }
    function setLastname(name) {
        lastname = name;
    }    
}
mjs
  • 21,431
  • 31
  • 118
  • 200
0

Others have given you the get answer, but your question seemed more about setting the value.

var foo = (function() {
    //Private vars
    var a = 1;

That is a one-of assignment to a, which is local to the anonymous function.

    return {
        //Public vars/methods
        a: a,

That is also a one-of assignment of the value of a to an a property of the object that will be referenced by foo. Subsequent changes to the variable a will not affect the value of this property.

        changeVar: function () {
            a = 2;

Here, a will resolve to a reference to the "outer" a, so it will change the value of the variable but it won't change the value of foo.a. If you know it will always be called as a method of foo, they you can instead write:

        changeVar: function () {
            this.a = 2;

so that it resolves a as a property of foo, not the scope chain (and hence variable a).

        }
    }
})();
RobG
  • 142,382
  • 31
  • 172
  • 209
  • I stated in the OP that `this.a` references the public 'a' and not the private 'a' but I wanted to change both, is this incorrect? I modified the first solution of the accepted answer to make it a setter as well. `a: function(value) {if(arguments.length > 0) a = value; else return a;` – Michael Oct 08 '11 at 11:15
  • Yes, you can change both. Access one as the object property and the other as the variable on the scope chain, e.g. `changeValue: function(value){this.a = value; a = value;}`. – RobG Oct 09 '11 at 12:24
  • That's what you asked, isn't it? You can't use a single expression to change both, it requires two expressions. – RobG Oct 09 '11 at 23:21
  • Well merging them together through a getter/setter seems better. – Michael Oct 10 '11 at 03:36