I am currently learning about variables in JavaScript. I understand that functions can be stored into a variable but in what ideal situation to store a function in a variable.
CodePudding user response:
All functions, other than some uses of anonymous function expressions, are stored as variables in JavaScript. Most of the time when you write functions you will be saving them as variables. In JS, functions (and almost everything else) are a type of Object that have a name
property and code. When you look at a function declaration like this
function add(x, y) {
return x y;
}
what you are seeing is a function named add
being created and stored as a variable. Try it out by writing that function in your browser Console and hitting enter, then just type add
and it will return to you the function code. Type add.name
and it returns you the name, the string "add".
That syntax is actually saving the function with the name "add" as the add
variable. This is much the same as if you had written it like this:
let add = function add(x, y) {
return x y;
}
You could also write this function expression without the function name and still save it to the add
variable. The advantage of writing the name too used to be that that it allowed the function to refer to itself, and also made it easier to identify the function in a debugger. Modern JS interpreters will just infer the name of the function in an anonymous function expression when it is saved to a variable.
As an anonymous function expression, the code just looks like this:
let add = function (x, y) {
return x y;
}
This is a traditional anonymous function expression. Under the hood, this is the same as if you'd written it in with the newer arrow function syntax, like this:
let add = (x, y) => {
return x y;
}
One reason you would want to use an anonymous function that is not saved to a variable is if you are passing a function as a parameter to another function. The built in setTimeout method requires you to pass a function in its first parameter. As an example:
setTimeout(function () => {
console.log("Delayed for 1 second.");
}, 1000)
or using a more modern arrow function like this:
setTimeout(() => {
console.log("Delayed for 1 second.");
}, 1000)
These functions are not saved as variables and have no inferred name. Another common pattern you may see with anonymous functions that are not saved to variables is an Immediately Invoked Function Expression.
The primary difference with most use cases between writing a function as a declaration and as an expression is that function declarations, like other variables and Classes, are hoisted. This means that no matter what order in the code you place a function declaration, it will automatically be interpreted as if it had been placed at the top of its scope. This means that it is possible to use a function written as a function declaration before it is actually declared. This is not possible with function expressions, which cannot be used before they appear in the code. Best practice is to avoid needing hoisting at all and to simply place functions, classes, and other variables in their logical order. Not understanding how hoisting works while having unorderly code can lead to unpredictable behavior and errors.
CodePudding user response:
@StephenMIrving has already answered this well ( 1), but I did want to share an interesting "real world" example from Bootstrap 5.
During startup, component initialization functions are saved to an array called DOMContentLoadedCallbacks. And once the page is finished loading Bootstrap plays back the array so that components are initialized at the proper time and in the proper order. Being able to pass and store function references like this has many useful applications.
// Bootstrap v5.0.2 (https://getbootstrap.com/)
const DOMContentLoadedCallbacks = [];
const onDOMContentLoaded = callback => {
if (document.readyState === 'loading') {
// add listener on the first call when the document is in loading state
if (!DOMContentLoadedCallbacks.length) {
document.addEventListener('DOMContentLoaded', () => {
DOMContentLoadedCallbacks.forEach(callback => callback());
});
}
DOMContentLoadedCallbacks.push(callback);
} else {
callback();
}
};