With functions, there tend to be three distinct stages to them.
- Prepare the variables.
- Do something.
- Return or affect something.
Sometimes there is a preliminary guard-clause, but that’s the main structure of each function.
If you stay good with that, you won’t go far wrong.
Let’s do this as an example.
###Prepare the variables
What are we going to call the function and what arguments will it have? We have some options on how we might want to use it.
- panel.innerHTML = getProperties(window).join(', ');
- panel.innerHTML = showProperties(window);
- showProperties(window, panel);
- showArray(getProperties(window), panel);
All are valid, but the third one seems to make it easiest to use the function. With the understanding that function has two different tasks to perform. This should mean using two functions. One called getProperties, and the other called showProperties that uses the first function.
Our showProperties function has two main pieces of information that can change. The object that we want to see the properties of, and the target element within which to show that information.
What should the target be? Should it be a string value of an identifier, should it be a selector of some kind, or should it be an element itself?
- showProperties(window, “panel”);
- showProperties(window, “#panel”);
- showProperties(window, panel);
Let’s keep things simple by passing the actual element as the target.
Because the main purpose of the showProperties function is to show the element properties, I don’t think that it’s appropriate for the function to also focus its time on finding the properties, so we should use a separate function called getProperties to get them, thus simplifying what the functions need to deal with.
function getProperties(obj) {
...
}
function showProperties(obj, el) {
...
}
###Do something
The getProperties function can be handled in any number of ways. You could use Object.keys, but for maximum compatibility you can use the for…in technique.
We can use the same prepare/do/return structure with the getProperties function.
function getProperties(obj) {
// prepare
var prop = "",
props = [];
// do something
for (prop in obj) {
if (obj.hasOwnProperty) {
props[props.length] = prop;
}
}
// return
return props;
}
If we’re sure that the computers involved can all handle ES5, we can simplify this getProperties function to instead be:
function getProperties(obj) {
return Object.keys(obj);
}
###Return or affect something
The showProperties function is just going to get the array, and display it on the given element.
showProperties(obj, el) {
var props = getProperties(obj);
el.innerHTML = props.join(", ");
}
We can place these functions inside an IIFE too, so that the complexities of what’s being done are hidden behind it.
With the IIFE we can from inside, return only the showProperties function, which becomes the public access to what’s being done.
var showProps = (function () {
// functions here
return showProperties();
}());
The showProperties function will have access to other functions within the IIFE. Nothing else outside is allowed to know about them.
###The end result
In total this will end up being:
var showProps = (function () {
function getProperties(obj) {
return Object.keys(obj);
}
showProperties(obj, el) {
var props = getProperties(obj);
el.innerHTML = props.join(", ");
}
return showProperties();
}());
showProps(window, document.querySelector('#panel'));
The names showProps and showProperties don’t have to be different. I’ve just made them separate here for the sake of clarity.
When is it appropriate to use a variable instead of a direct reference such as querySelector? The general rule is that when used once it’s okay. Twice, consider using a variable. Three times, definately use a variable.
###Other considerations
Now that we have the IIFE structured code, we can consider making things simpler. We can get rid of the IIFE. If we don’t want a couple of different functions floating around, and we don’t intend for getProperties to be used by anything else, we can move the getProperties function inside of showProperties.
function showProperties(obj, el) {
function getProperties(obj) {
return Object.keys(obj);
}
var props = getProperties(obj);
el.innerHTML = props.join(", ");
}
showProperties(window, document.querySelector('#panel'));
Is that better? It seems to be, until other complexities require us to use an IIFE once again.
The last improvement is to use a panel variable, to be consistent with good practice, and help simplify how the showProps function is used.
function showProperties(obj, el) {
function getProperties(obj) {
return Object.keys(obj);
}
var props = getProperties(obj);
el.innerHTML = props.join(", ");
}
var panel = document.querySelector('#panel');
showProperties(window, panel);
It’s always a balancing act of needs and requirements that’s involved here.
Where we’re at right now looks to be a good balance of things at this stage.