Sergio and the sigil

JavaScript, inner functions and private members

Posted by Sergio on 2009-02-24
This post is part of a series called JavaScript Demystified.

In our last installment in this short JavaScript series we took a look at closures. In some of the examples, we saw functions being declared (and returned) inside other functions. The capability of declaring a function inside another one is not common to all languages — C# only added this ability in version 2.0, via anonymous delegates.

Here's an example of inner functions in use.

function printPriceLong(name, price, quantity, currency) {
	var formatCurrency = function(value) {
		return value + ' ' + currency;
	};
	return 'Item: ' + name + '\n' +
		'Unit price: ' + formatCurrency(price) + '\n' +
		'Quantity: ' + quantity + '\n' +
		'TOTAL: ' + formatCurrency(price * quantity);
}

alert( printPriceLong('100g Toblerone bar', 2.09, 3, 'USD') );
/* =>
Item: 100g Toblerone bar
Unit price: 2.09 USD
Quantity: 3
TOTAL: 6.27 USD
*/

If we try to call formatCurrency from anywhere outside of printPriceLong we are going to cause an error because formatCurrency is scoped only inside its parent function.

In this example we can also see closures in action once again. The currency value is referenced inside formatCurrency but it's declared in it's parent. It's a short-lived closure, mind you, because we are not returning that inner function. It's discarded as soon as the parent function exits.

Who said JavaScript objects can't have private members?

Developers sometimes get upset when they realize that anyone has read and write access to the fields and methods of their JavaScript objects. Most of us are used to work with languages that allow us to declare some of our object's members out of reach for the calling code. We say that these members are private and we don't want anyone changing or even seeing them.

Well, this is not exactly true. If you must have private members in your JavaScript objects, you can. Inner functions and closures will come to our rescue.

Let's build on our previous example. Let's create an OrderItem object that will hold those values (name, price, etc.) Let's assume we do not want anyone changing the object's price without changing the currency at the same time (to ensure some level of consistency.) We could code our object like this:

function OrderItem(productName, price, quantity, currency) {
	//regular properties
	this.name = productName;
	this.quantity = quantity;

	//read accessors
	this.getCurrency = function(){ return currency; };	
	this.getPrice = function(){ return price; };
	//write accessor
	this.setPrice = function(newPrice, newCurrency){
		if(typeof newPrice !== 'number' || newPrice < 0){
			throw { message:'invalid price' };
		}
		if(typeof newCurrency !== 'string'){
			throw { message:'invalid currency' };
		}
		price = newPrice;
		currency = newCurrency;
	};

	//a private function
	var formatCurrency = function(value) {
		return value + ' ' + currency;
	};
	
	//methods that need private members
	this.getUnitPriceString = function(){
		return formatCurrency(price);
	};
	this.getTotalPriceString = function(){
		return formatCurrency(price * quantity);
	};
}

OrderItem.prototype = {
	//overriding the string representation of the object
	toString: function(){
		return  'Item: ' + this.name + '\n' +
			'Unit price: ' + this.getUnitPriceString() + '\n' +
			'Quantity: ' + this.quantity + '\n' +
			'TOTAL: ' + this.getTotalPriceString();	
	}
};

That seems a bit long, but hopefully we can understand what's going on here. We are letting name and quantity be regular read/write properties but we never defined properties for price or currency. We made those two values accessible via the getPrice and getCurrency methods, respectively.

The trick here is that both getPrice and getCurrency are defined inside our constructor function so they have access to the local variables price and currency. They have access to these variables even after the constructor returns (thank you closures.)

The same can be said for the setPrice method. We will use this method when we need to change the object's price. It will force us to also provide a currency.

I'll leave the explanation of the methods getUnitPriceString and getTotalPriceString as an exercise for you.

Let's instantiate one of these objects and see it in action.

var item = new OrderItem('100g Toblerone bar', 2.09, 3, 'USD');
//public methods:
alert( item.getUnitPriceString() );
// => '2.09 USD'
alert( item.getTotalPriceString() );
// => '6.27 USD'
alert(item); //this will use the toString() method
/* =>
Item: 100g Toblerone bar
Unit price: 2.09 USD
Quantity: 3
TOTAL: 6.27 USD
*/

//changing private fields
item.setPrice(1.11, 'EUR');
alert( item );
/* =>
Item: 100g Toblerone bar
Unit price: 1.11 EUR   <-- it worked!
Quantity: 3
TOTAL: 3.33 EUR
*/

//proving that price is not a field
item.price = '5.00';
alert( item.getUnitPriceString() );
// => '1.11 EUR' <-- Gotcha, smart pants!

item.setPrice(2);
//ERROR: message = 'invalid currency'
alert( item.formatCurrency(1.23) );
//ERROR: item.formatCurrency is not a function

And what am I supposed to do with this information?

I have yet to find the need to use private members in my JavaScript objects. Maybe that's because I am not shipping any JavaScript library with complex enough objects.

I think it's nice to know that you can create those off-limits values in your object. Hopefully when the need for such thing arises, we won't just say Oh, no! Can't do that!.

What about you? Have you found a use for private members in your JavaScript code? How did you get around or implemented it?