X

Sundararajan's Weblog

  • Java
    November 1, 2005

Encapsulation in JavaScript

Guest Author
In JavaScript, all members (fields and methods) of any object are public.
There is no equivalent of private, protected access of Java. What
if we want to have encapsulate fields in JavaScript? The trick is to use
Closures
.
If you know what closures are, then skip next section.

Closures in JavaScript


In JavaScript, a nested function can access locals, arguments of enclosing function
(can even assign to those -- unlike methods of Java anonymous/local class.
Java anonymous/local class methods can only access 'final' locals and arguments of the enclosing method)

function f(a) {
function g() {
// we access argument 'a' of enclosing function 'f'
return a++;
}
// we return the function 'g' here
return g;
}
x = f(10);
print(x()); // prints 10
print(x()); // prints 11
print(x()); // prints 12

In the example, 'x' acts as generator of sequence with 10 as starting point

Using closure to implement private fields

You may want to read Douglas Crockford's "Private Members in JavaScript" to see how closures are used to implement private instance fields.


Example with private instance fields:

function Employee(name, salary) {
// name and salary are private instance fields
this.getName = function() {
return name;
};
this.getSalary = function() {
return salary;
};
this.setSalary = function(amount) {
salary = amount;
};
this.increaseSalary = function(increment) {
if (increment < 0.0) {
throw "Huh! can't decrement!";
}
salary += increment;
};
}
var e = new Employee('Mr. X', 10000);
print(e.getSalary());
e.increaseSalary(1000);
print(e.getSalary());
e.increaseSalary(-100);

The above looks lot similar to the way objects are defined in the
programming language E.

Okay, now we have got private field and getter/setter methods or getter method only
(for read-only field such as name in the above example). But, the caller has to use method
calls instead of 'natural' field access syntax. What if we want to have field access like
syntax and still encaptulate the access by getter/setter? Yes, we can use
JSAdapter


function GetterSetterWrapper(obj) {
return new JSAdapter() {
__has__ : function(name) {
// check whether obj has the property or getter method
return name in obj ||
typeof(obj['get' + name]) == 'function';
},
__get__: function(name) {
if (name in obj) {
return obj[name];
} else {
var getter = 'get' + name;
// if there is getter method, then call it
if (typeof(obj[getter]) == 'function') {
return obj[getter]();
} else {
return undefined;
}
}
},
__put__: function(name, value) {
var setter = 'set' + name;
// if there is setter method, call it
if (typeof(obj[setter]) == 'function') {
return obj[setter](value);
} else {
// just set the property
return obj[name] = value;
}
}
}
}

With above wrapper function, we can write:

// wrap the employee object with get-set wrapper
var e = GetterSetterWrapper(new Employee('Mr. X', 10000));
print(e.getSalary());
// or equivalently
print(e.Salary);
e.increaseSalary(1000);
// or equivalently
e.Salary += 1000;
print(e.getSalary());
// or equivalently
print(e.Salary);
e.increaseSalary(-100);
// or equivalently
e.Salary -= 100;

Ah! It looks like we can have the cake and eat it as well! Note: I've not implemented JavaBean
convention for property names (Note the use of e.Salary instead of e.salary). As usual,
that is left as an exercise to reader :-) (hint: modify __get__, __put__ and __has__ methods
above to implement JavaBean convention).

Be the first to comment

Comments ( 0 )
Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.