JavaScript Encapsulation: Privacy and Data Hiding

JavaScript does not have syntax-level encapsulation. All properties of an object are visible. However, there do exist various patterns to ensure data encapsulation.

Singletons and the Module Pattern

The following code creates one singleton object using an object literal.

var point = {
	x: 0,
	y: 0,
	add: function() {
		return this.x + this.y;
	}
};

JavaScript closure allows us to use a function scope to hide the data. In the following code, we create and invoke an anonymous function; inside the function, we define local variables which are in the function’s scope, and return an object literal containing only the methods. This is the so-called module pattern.

var point = function () {
	var x = 0, y = 0;
	return {
		getX: function() { return x; },
		setX: function(_x) { x = _x; },
		getY: function() { return y; },
		setY: function(_y) { y = _y; },
		add: function() { return x + y; }
	};
}();
point.setX(3);
console.log(point.getX());	// 3

Warning: Note that this pattern is not suitable for creating multiple objects of the same type; it does not work for inheritance, either. The following code creates another object using Object.create; however, the new object refers to the same function scope as the old one.

p = Object.create(point);
p.setX(200);
console.log(p.getX());		// 200
console.log(point.getX());	// 200

Using a Constructor

Now we consider objects created using constructors and the new operator.

A common practice of data hiding is to use weird names for private members to pretend that other users will not access them. Of course this is cheating; however, it’s widely used.

function Point(x, y) {
	this._x = x || 0;
	this._y = y || 0;
}
Point.prototype = {
	getX: function() { return this._x; },
	setX: function(x) { this._x = x; },
	getY: function() { return this._y; },
	setY: function(y) { this._y = y; },
	add: function() { return this._x + this._y; }
};

var p = new Point(3, 4);
p.setX(100);
console.log(p.getX());

Another way is to add methods inside the constructor instead of in the prototype, and using local variables for private members. This is not efficient since the methods will be created each time the constructor is called. Also, this does not work for inheritance.

function Point(x, y) {
	var _x = x || 0;
	var _y = y || 0;
	this.getX = function() {
		return _x;
	};
	this.setX = function(x) {
		_x = x;
	};
	this.getY = function() {
		return _y;
	};
	this.setY = function(y) {
		_y = y;
	};
	this.add = function() {
		return _x + _y;
	};
}
var p = new Point(3, 4);
var q = new Point(5, 6);
console.log(p.getX());
console.log(q.getX());

Comments

comments