Search code examples
javascriptinheritanceanonymous-functioniife

Object.Prototype Methods and 'Use Strict' in an IIFE (Immediately-Invoked Function Expression)


The original Code:

'use strict';
function GitJs(config) {
    var defaults = {
        inheriting: false,
        clientId: undefined,
        accessToken: undefined,
        baseUrl: 'https://api.github.com',
        mode: 'read'
    };

    this.config = $.extend(defaults, config);
}

/**
 * Gets the jQuery method that GitJs#generateApiRequest is going to use to send the ajax request.
 *
 * @param {string} httpVerb The HTTP verb that the request will use,
 * @return string
 */
GitJs.prototype.getCommandMethod = function (httpVerb) {
    var method = $.get;

    switch (httpVerb) {
    case 'GET':
        method = $.get;
        break;
    case 'POST':
        method = $.post;
        break;
    }
    return method;
};

...

The new code:

(function() {
'use strict';
    'use strict';
    function GitJs(config) {
        var defaults = {
            inheriting: false,
            clientId: undefined,
            accessToken: undefined,
            baseUrl: 'https://api.github.com',
            mode: 'read'
        };

        this.config = $.extend(defaults, config);
    }

    /**
     * Gets the jQuery method that GitJs#generateApiRequest is going to use to send the ajax request.
     *
     * @param {string} httpVerb The HTTP verb that the request will use,
     * @return string
     */
    GitJs.prototype.getCommandMethod = function (httpVerb) {
        var method = $.get;

        switch (httpVerb) {
        case 'GET':
            method = $.get;
            break;
        case 'POST':
            method = $.post;
            break;
        }
        return method;
    };

    ...
}());

As this code stands, when I attempt:

var gitjs = new GitJs();

I am told that GitJs is undefined

What the hell I was thinking:

  • I don't want to put use strict inside of every method.
  • I want my code to play nice if it gets minified and concatenated to another file.
  • I want to use the .prototype syntax for ease of inheritance later on (and code clarity)
  • I don't want to make a global var gitJs variable because it could be overridden by someone else's script.
  • I assume that the user will always invoke the object constructor via the new keyword

For the record, I know I'm wrong. Way wrong. I just can't seem to figure out where the flaw in my thinking lies and I'd love some guidance.


Solution

  • Your problem is that GitJS is now a private variable of the immediately invoked function. You can't hide your function in a private scope and make it publicly available at the same time. They are mutually exclusive goals.

    Therefore, you will want to either explicitely set the global variable, via the window

    var GitJS;
    (function() {
        'use strict';
         GitJS = function(){ ... }
         ...
    }());
    

    or return the exported function from inside the IIFE.

    var ExportedGitJS = (function(){ //using a different name just to be clear...
        'use strict';
        var GitJS = function(){ ... }
        ...
        return GitJS;
    }());
    

    OK, I lied. You can make Javascript modules without having to rely on global variables but that usualy means also using a different module-creation convention and/or using a module library. I would highly recommend that you check out http://requirejs.org/ if you are interested in this.