Reputation: 1232
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
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
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
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