Marcus
Marcus

Reputation: 1232

Javascript object: How to create a working object inclusive encapsulation?

I want to use objectoriented Javascript, but the behaviour of my "object" is not as I expect them to be.

This is my object:

var Test = function(a){
    var variable = a;

    this.getA = function(){
        return this.variable;
    }

    this.setA = function(a){
        this.variable = a;
    }
}

Now I run this code:

var a = new Test("foo");
var b = new Test("bar");

alert(a.getA());                //undefined, but "foo" expected
alert(a.getA() == b.getA());    //true, but false expected

alert(a.variable);              //undefined, as expected

a.variable = "whatever";
alert(a.getA());                //"whatever", but undefined expected
alert(a.variable);              //"whatever", but undefined expected

a.setA("somewhere");
alert(a.getA());                //"somewhere", as expected
alert(a.variable);              //"somewhere", but undefined expected

Does anyone know how to create a functioning object, without the need to call "setA(a)" at the beginning, and with object encapsulation not allowing a.variable = "whatever";?

Upvotes: 0

Views: 160

Answers (3)

LetterEh
LetterEh

Reputation: 26696

Any this referenced in JS is going to be publicly accessible.

However, thanks to the concept of closure, you can have a fully private state, just by declaring variables (or functions) inside of your function.

Moreover, unless you're looking to get prototypical with your coding, you don't even need the new, there, nor do you need the this.

An example:

var makePerson = function (secret) {

    var is_alive = true,

        sayName = function () {
            if (!is_alive) { return; }
            console.log("My name is " + secret.name + ".");
        },

        sayAge = function () {
            if (!is_alive) { return; }
            console.log("My age is " + secret.age + ".");
        },

        haveBirthday = function () {
            if (!is_alive) { return; }
            secret.name += 1;
            console.log("I'm " + secret.name + 
                        ", and today I turned " + secret.age + ".");
        },

        die = function () {
            is_alive = false;
            console.log(secret.name + " died today;" +
                        " they were " + secret.age + " year" +
                        (secret.age > 1 ? "s" : "") + " old."   );
        },

        public_interface = { sayName      : sayName,
                             sayAge       : sayAge,
                             haveBirthday : haveBirthday,
                             die          : die           };

    return public_interface;
};


var bob = makePerson({ name : "Bob", age : 32 });

Nobody can touch secret.name, secret.age or is_alive from the outside.

bob.name; // undefined
bob.is_alive = false;  // Doesn't matter -- the code isn't relying on a public property.


bob.sayAge();       // "My age is 32"
bob.haveBirthday(); // "I'm Bob, and today I turned 33"
bob.die();          // "Bob died today; they were 33 years old"

Even better, JS allows you to change assignments at any time, so you might be worried about something like this:

// person overwriting the function
bob.haveBirthday = function () { is_alive = false; };

...but there is absolutely no reason to fear somebody doing that, because they have no access to those internal properties.

bob.haveBirthday(); // window.is_alive = false;
bob.sayAge(); "My age is 33";

Likewise if you try to rewrite a function to steal a value:

bob.sayName = function () { return secret.name; };
bob.sayName(); // window.secret.name;  OR  ERROR: can't call `name` of `undefined`

ANY functions which are not defined INSIDE of the function have NO access to the internal properties or methods.

And you can have private methods in the exact same way -- in fact, all of those functions were private, until I attached them all to the public_interface object (which is what I returned).

This is important to remember if you start using prototype.

var Thing = function (secret) {};
Thing.prototype.sayName = function () { console.log("My name is " + secret.name); };
var thing = new Thing({ name : "Bob", age : 32 });

thing.sayName(); // "My name is undefined"

Not even prototype functions have access to private state -- they MUST use public state (through this).

There are more-complex structures that you could use, making multiple closures to have public, private, private static and public static (which is what prototype is most like) properties, but that's a little more advanced and in the beginning, a little more confusing.

Upvotes: 1

Marcus
Marcus

Reputation: 1232

Just for fun (or for demonstration purpose), I found another solution in the meanwhile:

var Test = function(x){
    var variables = function(){}
    variables.variable = x; 

    this.getA = function(){
        return variables.variable;
    }

    this.setA = function(x){
        variables.variable = x;
    }
}

The test results:

    var a = new Test("foo");
    var b = new Test("baz");

    alert(a.getA());                //"foo" as expected
    alert(a.getA() == b.getA());    //false as expected

    a.variable = "whatever";
    alert(a.getA());                //"foo" as expected
    alert(a.variable);              //"whatever", doesn't seem preventable

    a.setA("somewhere");
    alert(a.getA());                //"somewhere", as expected
    alert(a.variable);              //"whatever", doesn't seem preventable

Upvotes: 0

zzzzBov
zzzzBov

Reputation: 179096

If you're encapsulating the value of a, you don't need to use this.variable to access the value, simply changing this.variable to variable should fix the issue.

Variables have functional scope, so var variable defines a variable that is in scope not only for Test, but also for setA and getA:

var Test = function(a){
    var variable = a;

    this.getA = function(){
        //you want to access the variable,
        //not the property on the instance
        return variable;

    }

    this.setA = function(a){
        variable = a;
    }
}

Upvotes: 2

Related Questions