declare the function
var add = function(x, y) {
return x + y;
};
now we can call the function using the variable add
> add(2, 3)
> 5
We can also reference the function without calling it, if we don’t add parenthesis and arguments:
> add
> function (x,y) { return x+y; }
2 things happen when we call add(5, 3)
:
First, the add
part is replaced (or interpreted) by the actual function declaration (function (x, y) { return x + y; }
,
then the parenthesis with the arguments are added, and the function runs.
So add(3, 5)
is the same as:
> function(x, y) {
> return x + y;
> }(3, 5)
> 5
A closure if a function that returns another function.
Let's leverage the add
function we just used to create a new function called addTen
. It allows us to add 10 to any number.
var addTen = function(x) {
return add(x, 10);
}
The same way, we can now reference this function with the variable addTen
. We must add parenthesis to run the function:
> addTen
> function (x) { return add(x, 10) }
>
> addTen(5)
> 15
Note that now, add(x, 10)
gets printed when we reference the function. It looks like we are calling add
since there are parenthesis, but the function isn’t being called yet.
The reason is Javascript’s double interpretation cycle. The code is interpreted twice: once at load time (when the script is loaded) and then at run time (when the code is actually called). The first time the code is interpreted:
- all variables are replaced by what they actually hold. For instance,
add
would be replaced byfunction (x, y) { return x + y;}
- all first level function calls are ran.
add(5,3)
would run and be replaced by15
What's important is "first level". In the example of our closure, the return add(x, 10)
part is "nested". It's inside another function, so it won't run unless the parent function is ran.
Let's look at an example; Say you load the following file:
var add = function (x, y) {
return x + y;
};
var addTen = function (x) {
return add(x, 10);
};
add
add(5, 3)
addTen
addTen(5)
the interpreted file, once loaded, would look like this: (this is not totally true but irrelevant here)
var add = function (x, y) {
return x + y;
};
var addTen = function (x) {
return add(x, 10);
};
function (x, y) {
return x + y;
}
8
function (x) {
return add(x, 10);
}
15
In the example of our closure, when interpreting addTen
, the part with add(x, 10)
isn’t executed despite parenthesis. That’s because the parent is not being ran (addTen
is without parenthesis).
Having the function declaration in the variable is not only useful to be able to call it multiple times at multiple places, but also to "pass it around". For example, to pass it as an argument to another function.
Let's say we have more math functions than just add. multiple
, for example:
var multiple = function (x, y) {
return x * y;
};
And instead of the addTen
function, we want an modeTen
function, which takes a number and an incrementation mode (add or multiple) and will combine the number and 10 using the incrementation mode passed to it:
var modeTen = function(x, mode) {
return mode(x, 10);
};
Here we can now call our modeTen
function with a number and either our add
or multiple
function:
> modeTen(5, add)
> 15
> modeTen(5, multiple)
> 50
It matters to understand how functions are referenced, passed and called because this is something we keep doing in javascript when dealing, for example, with callbacks:
var functionWithCallback = function (name, callback) {
console.log("hello " + name);
callback(name);
};
This is a very simple function that prints a name and calls a callback once it’s done.
The callback function could be just a function we use to let us know that the name was successfully printed:
var callback = function(name) {
console.log("successfully printed " + name);
};
> functionWithCallback('jonathan', callback);
> "hello jonathan"
> "successfully printed jonathan"
Closures are often wrongfully abused to fix a bug coming from a bad understanding of all this; Look at an example using our previous functionWithCallback
. Let's say you are using an API you are not really familiar with (could be something like res.render
in nodejs or anything), and you want to make sure that your callback will be called with the name
variable, you may write something like this:
functionWithCallback('jonathan', callback('jonathan'));
Note that you are passing callback('jonathan')
as an argument. As we've just seen, callback('jonathan')
will actually be called at load time, so you end up passing the result of the function instead of the function itself: functionWithCallback('jonathan', 'successfully printed jonathan')
. Obviously not what you wanted.
But now faced with a bug, and not understanding the issue, most engineers with mess around and end up solving this problem with a fixture:
functionWithCallback('jonathan', function() {
callback('jonathan');
});
Engineers often end up in this situation because this callback syntax looks familiar, as it is the syntax used to declare a callback inline, when it's not in a variable.
As we know, the callback
function is actually being called inside functionWithCallback
with name
passed to it as we want, so it’s enough to simply pass a reference of the callback
function as argument; As we saw, using the callback
variable without parenthesis:
functionWithCallback(‘jonathan’, callback);
Another example is in React, when passing a function as a prop. Let’s say we have the following component:
class Parent extends React.Component {
onButtonClick() {
console.log(‘clicked button‘);
}
render() {
<Button id=”1” onClick={this.onButtonClick} />
}
}
In this example, we to pass the onButtonClick
to the button component. And when then button is clicked, the function is called and all is good.
The issue here is that if the onButtonClick
function had a id
argument, the button would not know about what argument to pass:
class Parent extends React.Component {
onButtonClick(id) {
console.log(‘clicked button ‘ + id); // note now we pass an id
}
render() {
<Button id=”1” onClick={this.onButtonClick} /> // Button has no idea what “id” is
}
}
So we need to explicitly tell the button what the id is and to pass it as an argument when calling the function.
One of the solution is to use a closure:
render() {
<Button id=”1” onClick={() => this.onButtonClick(“1”)} />
}
In this case it would work, because thanks to the closure the inner function this.onButtonClick(“1”)
is nested and doesn’t get called at load time.
Another solution would be to use an inner onClick function on the button component, which is often cleaner (in my opinion):
class Parent extends React.Component {
onButtonClick(id) {
console.log(‘clicked button ‘ + id);
}
render() {
<Button id=”1” onClick={this.onButtonClick} />
}
}
Class Button extends React.Component {
onClick() {
this.props.onClick(this.props.id);
}
}
This is also only possible if the element receiving the function as props is a component you control; If the component was a native HTML <button>
for example, this wouldn’t be an option.
A last general comment about passing functions through multiple levels. In our example of the onButtonClick
that takes an id
as argument, you only have to explicitly pass the id
argument in the component that will actually call the function, and can only pass the function by reference to other components it goes through;
class Root extends React.Component {
onButtonClick(id) {
console.log(‘clicked button ‘ + id);
}
render() {
<Section onButtonClick={this.onButtonClick} />
}
}
Class Section extends React.Component {
render() {
<button id=”1” onClick={() => this.onButtonClick(“1”)} />
}
}
Note here the onButtonClick
is from the Root
component and we pass the function down to a button element, through the Section
component. Despite choosing the closure solution, which we use on the props of the button
, we don’t have to explicitly tell Section
about the id
argument and just pass it onButtonClick
as a reference.