Today, I came across of a code snippet that made me pause and squint. The author, who was new to the JavaScript world, attempted to wrap a part of his code in an anonymous IIFE (Immediately Invoked Functional Expression). I have been a party to many a heated discussion on the merits of IIFE as a coding pattern but what made me pause and squint this time was not just the IIFE itself, it was the way it was used.
In short, the code looked somewhat like this:
function doSomethingImportant() {
const username = document.forms["login_form"]["username"].value
if (!username) {
(function(){
// Display error here
console.log('Missing username')
})
return false
}
}
...
Correct. The anonymous function is never called, thus the error message is never displayed making the author question the reality. Aside from this obvious oversight, the snippet brings up a more interesting question: where are we allowed to declare a function?
Many developers coming from a different language, such as Java, C# or C++, are used to more or less strict rules that dictate where and how functions are declared and used.
First, we declare a function - typically, at the top of the file (or in a class if it's a method). Then, often times some hundreds of lines down the code, we call it. Not the other way around.
JavaScript throws a curve ball at the uninitiated by following a somewhat unique set of rules called "hoisting" (described here), which means that the function declarations (and the vars, too, but not block-bound "const" and "let") are automatically moved to the top of the enclosing scope.
Contrary to the Java and C# norms though, JavaScript "scope" is usually the enclosing function - or the global scope. Which means that it does not really matter where the function is defined - before the call or after, as long as the call happens within the same scope as the function declaration.
Hoisting is a convenient way of getting around the "chicken or egg" problem but it also may create some rather amusing situations. Consider the following snippet:
function dummy(arg) {
if (arg) {
show_error() // <--- Where is the function declaration??
return false
} else {
return true
}
// Here it is, JS silently moved it to the top!
function show_error() {
console.log('Error')
}
}
Rather counter-intuitive but perfectly legal!