Monday, 9 October, 2017 UTC


Summary

The following is a short extract from our new book, JavaScript: Novice to Ninja, 2nd Edition, written by Darren Jones. It's the ultimate beginner's guide to JavaScript. SitePoint Premium members get access with their membership, or you can buy a copy in stores worldwide.
The dynamic nature of JavaScript means that a function is able to not only call itself, but define itself, and even redefine itself. This is done by assigning an anonymous function to a variable that has the same name as the function.
Consider the following function:
function party(){
console.log('Wow this is amazing!');
party = function(){
    console.log('Been there, got the T-Shirt');
}
}
            
This logs a message in the console, then redefines itself to log a different message in the console. When the function has been called once, it will be as if it was defined like this:
function party() {
console.log('Been there, got the T-Shirt');
}
            
Every time the function is called after the first time, it will log the message “Been there, got the T-Shirt”:
party();
<< 'Wow this is amazing!'

party();
<< 'Been there, got the T-Shirt'

party();
<< 'Been there, got the T-Shirt'
            
If the function is also assigned to another variable, this variable will maintain the original function definition and not be rewritten. This is because the original function is assigned to a variable, then within the function, a variable with the same name as the function is assigned to a different function. You can see an example of this if we create a variable called beachParty that is assigned to the party() function before it is called for the first time and redefined:
function party(){
console.log('Wow this is amazing!');
party = function(){
    console.log('Been there, got the T-Shirt');
}
}

const beachParty = party; // note that the party function has not been invoked

beachParty(); // the party() function has now been redefined, even though it hasn't been called explicitly
<< 'Wow this is amazing!'

party(); 
<< 'Been there, got the T-Shirt'

beachParty(); // but this function hasn't been redefined
<< 'Wow this is amazing!'

beachParty(); // no matter how many times this is called it will remain the same
<< 'Wow this is amazing!'
            

Losing Properties

Be careful: if any properties have previously been set on the function, these will be lost when the function redefines itself. In the previous example, we can set a music property, and see that it no longer exists after the function has been invoked and redefined:
function party() {
console.log('Wow this is amazing!');
party = function(){
console.log('Been there, got the T-Shirt');
}
}

party.music = 'Classical Jazz'; // set a property of the function

party();
<< "Wow this is amazing!"

party.music; // function has now been redefined, so the property doesn't exist
<< undefined
This is called the Lazy Definition Pattern and is often used when some initialization code is required the first time it’s invoked. This means the initialization can be done the first time it’s called, then the function can be redefined to what you want it to be for every subsequent invocation.

Init-Time Branching

This technique can be used with the feature detection that we discussed in the last chapter to create functions that rewrite themselves, known as init-time branching. This enables the functions to work more effectively in the browser, and avoid checking for features every time they’re invoked.
Let’s take the example of our fictional unicorn object that’s yet to have full support in all browsers. In the last chapter, we looked at how we can use feature detection to check if this is supported. Now we can go one step further: we can define a function based on whether certain methods are supported. This means we only need to check for support the first time the function is called:
function ride(){
    if (window.unicorn) { 
        ride = function(){
        // some code that uses the brand new and sparkly unicorn methods
        return 'Riding on a unicorn is the best!';
    }
    } else {
        ride = function(){
        // some code that uses the older pony methods
        return 'Riding on a pony is still pretty good';
    }
    }
    return ride();
}
            
After we’ve checked whether the window.unicorn object exists (by checking to see if it’s truthy), we’ve rewritten the ride() function according to the outcome. Right at the end of the function, we call it again so that the rewritten function is now invoked, and the relevant value returned. One thing to be aware of is that the function is invoked twice the first time, although it becomes more efficient each subsequent time it’s invoked. Let’s take a look at how it works:
Continue reading %JavaScript Functions That Define and Rewrite Themselves%