Tuesday 22 November 2016

Javascript: apply

In You Dont Know JS Chapter 5: Scope Closure, there is following code snippet to illustrate the power of module pattern.

var MyModules = (function Manager() {
    var modules = {};

    function define(name, deps, impl) {
        for (var i=0; i<deps.length; i++) {
            deps[i] = modules[deps[i]];
        }
        modules[name] = impl.apply(impl, deps);
        console.log("module "+modules[name]);
    }

    function get(name) {
        return modules[name];
    }

    return {
        define: define,
        get: get
    };
})();

MyModules.define( "bar", [], function(){
    function hello(who) {
        return "Let me introduce: " + who;
    }

    return {
        hello: hello
    };
} );

MyModules.define( "foo", ["bar"], function(bar){
    var hungry = "hippo";

    function awesome() {
        console.log( bar.hello( hungry ).toUpperCase() );
    }

    return {
        awesome: awesome
    };
} );

var bar = MyModules.get( "bar" );
var foo = MyModules.get( "foo" );

console.log(bar.hello( "hippo" )); // Let me introduce: hippo

foo.awesome(); // LET ME INTRODUCE: HIPPO

What does this line do?
modules[name] = impl.apply(impl, deps);

It is another way of invoking a function, sort of like Java reflection. And we get to pass an array variable in the second argument to replace a list of parameters. What is the first argument for?

According  to https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply

fun.apply(thisArg, [argsArray])

Parameters

thisArg
The value of this provided for the call to fun. Note that this may not be the actual value seen by the method: if the method is a function in non-strict mode code, null and undefined will be replaced with the global object, and primitive values will be boxed.
I don't quite understand this definition. As far as I am concerned,

fun.apply(thisArg, [argsArray]) = thisArg.fun(arg1, arg2....)

In this case, I also don't understand why the first parameter is 'impl'. But I have proved it can be replaced with 'null'.

The next question is why the function needs to be called in such a unconventional way. Could it be called normally?

If we put

modules[name] = impl(deps);

bar.hello() executes OK, but foo.awsome() throws TypeError: bar.hello is not a function.

Then try

modules[name] = impl(deps[0]);

It does work. But obviously it's not a universal solution. Hence the use of apply().

No comments:

Post a Comment