javascriptfunctionextend

Javascript: Extend a Function


The main reason why I want it is that I want to extend my initialize function.

Something like this:

// main.js

window.onload = init();
function init(){
     doSomething();
}

// extend.js

function extends init(){
    doSomethingHereToo();
}

So I want to extend a function like I extend a class in PHP.

And I would like to extend it from other files too, so for example I have the original init function in main.js and the extended function in extended.js.


Solution

  • With a wider view of what you're actually trying to do and the context in which you're doing it, I'm sure we could give you a better answer than the literal answer to your question.

    But here's a literal answer:

    If you're assigning these functions to some property somewhere, you can wrap the original function and put your replacement on the property instead:

    // Original code in main.js
    var theProperty = init;
    
    function init(){
         doSomething();
    }
    
    // Extending it by replacing and wrapping, in extended.js
    theProperty = (function(old) {
        function extendsInit() {
            old();
            doSomething();
        }
    
        return extendsInit;
    })(theProperty);
    

    If your functions aren't already on an object, you'd probably want to put them there to facilitate the above. For instance:

    // In main.js
    var MyLibrary = {
        init: function init() {
        }
    };
    
    // In extended.js
    (function() {
        var oldInit = MyLibrary.init;
        MyLibrary.init = extendedInit;
        function extendedInit() {
            oldInit.call(MyLibrary); // Use #call in case `init` uses `this`
            doSomething();
        }
    })();
    

    But there are better ways to do that. Like for instance, providing a means of registering init functions.

    // In main.js
    var MyLibrary = (function() {
        var initFunctions = [];
        return {
            init: function init() {
                var fns = initFunctions;
                initFunctions = undefined;
                for (var index = 0; index < fns.length; ++index) {
                    try { fns[index](); } catch (e) { }
                }
            },
            addInitFunction: function addInitFunction(fn) {
                if (initFunctions) {
                    // Init hasn't run yet, remember it
                    initFunctions.push(fn);
                } else {
                    // `init` has already run, call it almost immediately
                    // but *asynchronously* (so the caller never sees the
                    // call synchronously)
                    setTimeout(fn, 0);
                }
            }
        };
    })();
    

    Here in 2020 (or really any time after ~2016), that can be written a bit more compactly:

    // In main.js
    const MyLibrary = (() => {
        let initFunctions = [];
        return {
            init() {
                const fns = initFunctions;
                initFunctions = undefined;
                for (const fn of fns) {
                    try { fn(); } catch (e) { }
                }
            },
            addInitFunction(fn) {
                if (initFunctions) {
                    // Init hasn't run yet, remember it
                    initFunctions.push(fn);
                } else {
                    // `init` has already run, call it almost immediately
                    // but *asynchronously* (so the caller never sees the
                    // call synchronously)
                    setTimeout(fn, 0);
                    // Or: `Promise.resolve().then(() => fn());`
                    // (Not `.then(fn)` just to avoid passing it an argument)
                }
            }
        };
    })();