Expose AMD module globally when not in a RequireJS environment - javascript

I am attempting to convert a classic JavaScript "class" into an AMD module. However, I also need to keep exporting the class into the global namespace because some legacy code needs it. I've tried this, however, the global object is not created. What am I doing wrong?
define('VisitorManager', function () {
var VisitorManager = function () {
"use strict";
// ...
};
VisitorManager.prototype.hasExistingChat = function () {
// ...
};
//expose globally
this.VisitorManager = VisitorManager;
//return AMD module
return VisitorManager;
});

To expose your module globally, you need to register it in the global object.
In the browser the global object is window:
window.VisitorManager = VisitorManager;
In a Node.js environment the global object is called GLOBAL:
GLOBAL.VisitorManager = VisitorManager;
To use the class in both a legacy environment and with RequireJS, you can use this trick:
(function() {
var module = function() {
var VisitorManager = function() {
"use strict";
// ...
};
// Return the class as an AMD module.
return VisitorManager;
};
if (typeof define === "function" && typeof require === "function") {
// If we are in a RequireJS environment, register the module.
define('VisitorManager', module);
} else {
// Otherwise, register it globally.
// This registers the class itself:
window.VisitorManager = module();
// If you want to great a global *instance* of the class, use this:
// window.VisitorManager = new (module())();
}
})();

Related

Why is IIFE required in the following context?

I am working to emulate the require functionality with a few javascript functions, for example:
// We set up an object that will include all the public modules
// And then a require function which basically just accesses that public module
const modules = {};
function require(moduleName) {
return modules[moduleName];
}
modules['math.js'] = function() {
// hidden implementation details
let incr = x => ++x;
return {
incr,
sum(x,y) { return x+y; }
}
}
Why is it required to do modules['math.js'] = (function() {...})(); and not just a normal non-invoked function call as above?
modules["match.js"] is only a function. You probably want it to be an object:
modules["math.js"] = { ... };
But then you won't have private members or functions you can use! Everything will be public. So to solve this we create a closure:
modules["math.js"] = function () {
let priv = 0;
return { ... };
};
Here's where we are now. To make it a closure AND have modules["math.js"] be a nice object we can use, we immediately call this function:
modules["math.js"] = (function () {
let priv = 0;
return { ... };
})();
Now, modules["math.js"] is an object with members and methods we can use, AND it has a private locally scoped variable named priv that can be used only by the module.
This is similar to ES6 modules where you export a select few values bound to identifiers and everything else in the file is private.

Use object properties without specifying the object in Javascript

The following script is a simple class register script. I don't want to store classes on the global window object, but still I would like to make new MyClass2(); work. If I would be able to define obj variable as global object like the window then the following code should work fine.
So every properties on the obj object should be available as variable inside the scope of the defineClass function.
Is it possible somehow or should I stack with the commented code?
(function() {
var obj = {};
window.defineClass = function(className, cb) {
obj[className] = cb.call(obj /*, obj*/ );
}
window.start = function() {
new obj.MyClass();
}
})();
defineClass('MyClass', function( /*obj*/ ) {
function MyClass() {
this.obj = new MyClass2();
//this.obj = new obj.MyClass2();
}
window.MyClass = MyClass;
return MyClass;
});
defineClass('MyClass2', function( /*obj*/ ) {
function MyClass2() {
console.log('MyClass2');
}
return MyClass2;
});
start()
If you want the notation new MyClass2() to work, either define function MyClass2(){} in global scope or closure, ex:
defineClass('MyClass', function() {
function MyClass() {
this.obj = new MyClass2();
}
window.MyClass = MyClass;
return MyClass;
function MyClass2(){}
});
window is a special global object as window.window is window itself.
Using module loader is handy for this kind of problem, ex. requirejs, ES6 import, etc.
Using iframe is another possible solution for your need.

Potential dangers of enabling classwide globals (Javascript)?

I have a question regarding the relative safeness of designing Javascript types that use class static/global variables. For example, suppose we had:
function whatever()
{
if(whatever.initialized === undefined)
{
whatever.global_setting = 1024;
whatever.initialized = true;
}
}
whatever.prototype.get_global_setting = function()
{
return whatever.global_setting;
}
whatever.prototype.set_global_setting = function(setting)
{
whatever.global_setting = setting;
}
Now if only one developer/team were to be using the library then whatever nonsense they decide to do on a class-wide scale wouldn't be such a big issue.
But what if we have scripts from several organizations running in a single webpage (ad partners and such) all of which are accessing the library from the same codebase (eg: "XYZ.com/scripts.js")? Will they all get their own private copy or is there even the slightest possibility that it might be shared? If so, then providing an interface for global settings could lead to some pretty serious problems!
EDIT
Just to be clear, my library is actually defined more along the lines of this:
var whatever =
(
function()
{
"use strict";
function internal_whatever()
{
if(internal_whatever.initialized === undefined)
{
internal_whatever.global_setting = true;
internal_whatever.initialized = true;
}
}
internal_whatever.prototype.get_global_setting = function()
{
return internal_whatever.global_setting;
}
internal_whatever.prototype.set_global_setting = function(setting)
{
internal_whatever.global_setting = setting;
}
return internal_whatever;
}
)();
if(typeof module !== "undefined" && module.hasOwnProperty("exports"))
{
module.exports = whatever;
}
Does that totally mitigate the issue, or are there still some corner-cases?
Well, you could use namespace scope instead of global scope. Like plugins do.
Simplified something like this:
;var Modules = (Modules === undefined) ? {} : Modules;
Modules.whatever = {
_nonecares: 1234,
whatever: function(){
return this._nonecares
}
};
console.log(Modules.whatever._nonecares);
This is how the newest jquery does it:
(function(global, factory){
"use strict";
// Pass this if window is not defined yet
})(typeof window !== "undefined" ? window : this, function( window, noGlobal){
"use strict";
// Expose jQuery and $ identifiers, even in AMD
// (#7102#comment:10, https://github.com/jquery/jquery/pull/557)
// and CommonJS for browser emulators (#13566)
if( !noGlobal ){
window.jQuery = window.$ = jQuery;
}
return jQuery;
});

Sharing nunjucks filters across node and browser

I am trying to find a way to get my filter works on both side.
Here is the code I have on node :
var env = nunjucks.configure(__dirname + '/../templates', {
express: app
});
env.addFilter('date', function(str){
return 'ok';
});
And on browser side :
var env = new nunjucks.Environment();
env.addFilter('date', function(str){
return 'ok';
});
I would like to have my filter in a place where it will be available in these two different environments but I don't find a solution to do so.
On client side I am using nunjucks-slim version. My templates are precompiled using gulp.
Thanks for your help !
You could put your filters in a separate file/module, where you pass env in as an argument.
eg.
/**
* #param env The nunjucks environment
*/
function(env){
env.addFilter('fancy', function(input){
return 'fancy ' + input
});
env.addFilter(...);
return env;
}
You could then use a UMD wrapper (https://github.com/umdjs/umd) to make it compatible with both browser and server. The finished wrapper might look something like this:
// custom_filters.js
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define([], factory);
} else if (typeof exports === 'object') {
// Node. Does not work with strict CommonJS, but
// only CommonJS-like environments that support module.exports,
// like Node.
module.exports = factory();
} else {
// Browser globals (root is window)
root.custom_filters = factory();
}
}(this, function () {
// Return a value to define the module export.
return function(env){
env.addFilter('fancy', function(input){
return 'fancy ' + input
});
return env;
};
}));
Then use it like this:
Node:
var env = nunjucks.configure(__dirname + '/../templates', {
express: app
});
require('./path/to/custom_filters')(env);
Browser (Globals):
var env = new nunjucks.Environment();
window.custom_filters(env);
Browser (AMD):
define(
['nunjucks', 'path/to/custom_filters'],
function(nunjucks, custom_filters){
var env = new nunjucks.Environment();
return custom_filters(env);
}
);

How to solve circular dependency in Require.js?

Basically, the idea is that "sub" module creates an object, and that object should be part of a utilities library which is the "main" module. However, the "sub" object depends on utilities from "main":
// Main module
define(['sub'], function(sub) {
var utils = {
utilityMain: function () {
// ...
};
// ...
};
tools.subModule = sub;
return tools;
});
// Sub module
define(['main'], function(main) {
return new (function () {
// Singleton object using functions in main module
var somestuff = function () {
main.utilityMain();
// etc
};
})();
});
How can I achieve this with require.js without creating a black hole that would swallow the whole planet?
Thank you very much.
There are a few things suggested in the docs:
b can fetch a later after modules have been defined by using the require() method (be sure to specify require as a dependency so the right context is used to look up a)
e.g.:
// Sub module
define(['require'], function(require) {
return new (function () {
// Singleton object using functions in main module
var somestuff = function () {
require('main').utilityMain();
// etc
};
})();
});
or
you could instead use exports to create an empty object for the module that is available immediately for reference by other modules
e.g.:
// Main module
define(['sub', 'exports'], function(sub, exports) {
exports.utilityMain: function () {
// ...
};
exports.subModule = sub.sub;
});
// Sub module
define(['main', 'exports'], function(main, exports) {
exports.sub = new (function () {
// Singleton object using functions in main module
var somestuff = function () {
main.utilityMain();
// etc
};
})();
});
and
Circular dependencies are rare, and usually a sign that you might want to rethink the design

Categories

Resources