TOC
Objects:

Property accessors (getters & setters)

We already discussed object properties, and how useful they are. By default, they are completely accessible to the consumer of the object, and while the creator and consumer of the object will often be the same person, sometimes its not.

When you are exposing an object to be used by other programmers, it can be very useful to take more control of how a property can be used. For this, we can use property accessors, often referred to as getters and setters.

When talking about property accessors, or getters and setters, we are actually talking about methods. However, they are special methods, which will appear as properties, while allowing you to do extra processing before assigning and/or returning the value of the property. Consider this regular example of an object with properties:

let user = 
{
	name: "John Doe",
	age: 42	
};

It defines two properties, name and age, which are completely accessible to the consumer of the object - they can read and write to these properties any way they want to, without any checks or logic being applied to the process. Let's try changing that.

getters

First, let's look at how getters work. As mentioned, getters and setters are just functions/methods, but they use the special get and set keywords to change their behavior from regular methods to getters and setters. Let's try adding a getter to our object:

let user = 
{
	_name: "John Doe",
	age: 42,
	
	get name()
	{
		return this._name;
	}
};

alert(user.name);

I have now added a very simple getter, called name, which will just return the value of the _name property. Notice that while it looks like a function, it can be referenced as a property, as it can be seen from the last line of the example.

setters

In the example above, we have not yet defined a setter for the name property. This essentially means that the value can't be written to from outside of the object - if you try, you will see that nothing is changed, as illustrated by this modified version of the first example:

let user = 
{
	_name: "John Doe",
	age: 42,
	
	get name()
	{
		return this._name;
	}
};
user.name = "test";
// John Doe - the property was not changed
alert(user.name);

Now let's change that, by adding a setter to the object. As you can see, it looks like a getter, but using the set keyword instead of get, and then it takes a parameter - JavaScript will automatically populate this parameter whenever you try to assign a value to the property:

let user = 
{
	_name: "John Doe",
	age: 42,
	
	get name()
	{
		return this._name;
	},
	
	set name(val)
	{
		this._name = val;
	}
};
user.name = "test";
// test
alert(user.name);

Now we can change the value of the property, but so far, we haven't really added any functionality, as compared to regular properties. Let's add some logic to our special methods.

Adding logic

We have now seen simple examples of both getters and setters, but as mentioned, the true benefit of using property accessors, or getters and setters, is the fact that we get more control of the input and output. With that in mind, I would like to show you a more complete example of an object with a getter and a setter, which will give you a better idea of what can be accomplished:

let user = 
{
	_name: "joHn dOE",
	age: 42,
	
	get name()
	{
		let arr = this._name.split(' ');
		arr = arr.map(function(part)
		{
			return part.charAt(0).toUpperCase() + part.slice(1).toLowerCase();
		});
		return arr.join(" ");
	},
	
	set name(val)
	{
		val = val.trim();
		if(val == '')
			alert("Empty value not allowed!");
		else
			this._name = val;
	}
};

//joHn dOE -> John Doe
alert(user.name);
user.name = prompt("Enter new name:");
alert(user.name);

First of all, notice how I have now messed up the casing of the name held by the _name property (joHn dOE). To take care of situations like this, in our getter, we now split the name into parts and make sure that each part of the name is lowercased, except for the first character, which is uppercased - in other words, our getter now properly formats the name as we would expect it to look like. This is a great example of how you can use getters to gain more control of your object properties.

I have also rewritten the setter to do some very basic validation - at this point, it only ensures that no empty values are assigned to the property, but this could obviously be expanded to handle more cases. If an empty value is passed in, an error is issued and the property is not set - you can try this when running the example.

Private properties

The brightest readers of this article might ask a very relevant question: What prevents the consumer of the object from simply accessing the _name property, and thereby circumventing the logic applied in the name getter and setter? In this case, the answer is, unfortunately: Nothing.

JavaScript does in fact offer the concept of private properties, but only for classes - with objects, everything is public by default and can't be made private. However, in JavaScript, as well as in some other programming languages, there's a generally accepted unwritten rule stating that properties starting with an underscore are to be considered as internal and not accessed directly.

So, if you define getters and setters for an existing property on the object, let the name of the backing property start with an underscore, to signal to the consumer that this specific property should not be accessed directly.

Summary

Property accessors, more commonly referred to as getters and setters, allows you to take full control of the properties of an object. Because getters and setters are functions/methods, you can add logic to the read/write process of the property, but from the outside you can still access the value as if it was a regular property.