Require.JS shim config global scope?

nimgrg picture nimgrg · Apr 2, 2013 · Viewed 13k times · Source

I have following setup for requireJS.

requirejs.config({
     paths: {
            'resources' : '/Scripts/resources'
     },
     shim: {
             'resources': {
                           exports: 'LocalizedStrings'
           }
     }
});

And my resources.JS look like following:

LocalizedStrings = {
                    title: "Demo",
                    save: "Save"
}

Now when I load resources as a dependency in main.JS file I can access LocalizedStrings and it works.

//main.js
define(function(require){
    var LocalizedStrings = require('resources');
    console.log(LocalizedStrings); //works as expected
});

However on other modules I don't really need to load the resources as dependency to access 'LocalizedStrings'.

//othermodule.js
define(function(require){
    console.log(LocalizedStrings); //works as expected even though resources dependency is not loaded
});

What I don't understand here is if I load a JS file using shim and load it once, does it become globally available and I don't have to load the same dependency again in other modules.

Answer

Paul Grime picture Paul Grime · Apr 2, 2013

Backbone and Underscore both modify the global scope, so if the browser has run their code, then the globals will exist.

This will happen if loading as a shim in RequireJS, or as if you included a script tag with their src directly.

Once the globals exist, they exist (unless explicitly deleted I guess).

This jsfiddle is a simple example of using shims, and seeing that the values (for certain libraries) are set as globals.

The aim of the example is to show that the value of the globals is only guaranteed inside a require() call. (if using an AMD loader, and not simply importing the libraries in a script tag). And that the values of the globals will exist at some indeterminate time in the future.

Source code

require.config({
    shim: {
        underscore: {
            exports: '_'
        },
        backbone: {
            deps: ["underscore", "jquery"],
            exports: "Backbone"
        }
    },
    paths: {
        jquery: "http://code.jquery.com/jquery-1.9.1",
        backbone: "http://backbonejs.org/backbone",
        underscore: "http://underscorejs.org/underscore"
    }
});

function appendVersions(msg, $, _, Backbone) {
    var pre = document.getElementById("content");
    var s = "<h2>" + msg + "</h2>";
    s += "jquery=";
    try { s += $.fn.jquery; } catch (e) { s += e.msg; }
    s += "<br>";
    s += "_=";
    try { s += _.VERSION; } catch (e) {  s += e.msg; }
    s += "<br>";
    s += "Backbone=";
    try { s += Backbone.VERSION; } catch (e) {  s += e.msg; }
    pre.innerHTML += s;
}

appendVersions("Before require (will be undefined)", window["$"], window["_"], window["Backbone"]);

require(["jquery", "underscore", "backbone"], function ($, _, Backbone) {
    appendVersions("Inside Require (should *always* be ok, unless the scripts aren't there)", $, _, Backbone);
});

appendVersions("After require (Probably be undefined, as the require probably won't have loaded the scripts yet)", window["$"], window["_"], window["Backbone"]);

setTimeout(function () {
    appendVersions("After Timeout (should be ok, but depends on how quickly the scripts load. Try changing the timeout duration)", window["$"], window["_"], window["Backbone"]);
}, 2000);

Sample output

enter image description here