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;
});
Related
I just want to ask for help regarding my problem, I have an external JS library and currently it is working when it's inside the tag in html, however when I try to incorporate it to my factory function you can see in the network that it has been read but when you try to use the global variable it is undefined, global variable is FlowchartLayout.
This is the snippet of the external JS library, I tried to make it an UMD module
(function (root, factory) {
if (typeof exports === 'object') {
// CommonJS
module.exports = factory(require('b'));
} else if (typeof define === 'function' && define.amd) {
// AMD
define(['b'], function (b) {
return (root.returnExportsGlobal = factory(b));
});
} else {
// Global Variables
root.returnExportsGlobal = factory(root.b);
}
}(this, function (b) {
'use strict';
function FlowchartLayout() {
go.Layout.call(this);
this._angle = 90;
this._nodeSpacing = 40; // between nodes in same layer
this._layerSpacing = 40; // between nodes in different layers
this._laneProperty = "lane"; // name of data property that identifies lane as a unique number or a string
this._laneComparer = null; // function for sorting FlowchartLanes
this._reset();
}
go.Diagram.inherit(FlowchartLayout, go.Layout);
/*The rest of the code of FlowchartLayout*/
return FlowchartLayout;
}));
Any help will be appreciated. Thank You!
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())();
}
})();
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);
}
);
I have an extend method included in my library, making it possible for methods to be added to the core library:
library.prototype.extend = function(name,plugin) {
library.prototype[name] = plugin.init;
for (var method in plugin) {
if(method !== 'init') {
library.prototype[name].prototype[method] = plugin[method];
}
}
};
In use it looks like so:
library.prototype.extend('aReallyInterestingPlugin', {
//the init method gets added to the base libraries prototype
init: function() {
this.shouldBePrivate;
},
//another other methods are created as a prototype of the newly added Plugin
anotherMethod : function() {
//this should have access to the shouldBePrivate var
}
});
Users are then able to call the plugin like so:
var test = new library();
test.aReallyInterestingPlugin();
This works but I'm not exactly happy with the approach and have been trying to find an alternative pattern to make this work.
The problem with it, is that the init and the anotherMethod are added directly to the libraries prototype chain so their scope is also the global libraries scope which is messy because if any instance variables are declared (like shouldBePrivate above) they are also added to the libraries prototype chain.
How can I enable the plugin to be added and have it's own private scope? One way I've thought of is that a plugin could always be called as a constructor (and will therefore have it's own scope and this context) but then I'm not sure how clean that is...for instance for that to work the user would have to do something like this when calling the plugin:
var test = new library();
test.aPlugin = new aReallyInterestingPlugin();
What you could do is to have the plugin method bound to a new object, so that they don't 'pollute' the Library.
But instead of having the plugin as a method of a Library instance, have it rather as a lazy getter, so the syntax is more fluid, and you can build a new instance only if required.
The plugin syntax could then be simplified : just use a javascript class => a function that has methods defined on its prototype.
I think also that it makes sense to have 'extend' as a property of Library, not a method set on its prototype, since no instance should make use of it.
With this you can add a plugin with
library.extend('aReallyInterestingPlugin',AReallyInterestingPluginClass);
to use you can write
var myLibrary = new Library();
myLibrary.somePlugin.someMethod(arg1, arg2, ...);
The code would look like :
library.extend = function(name,plugin) {
var pluginInstance = null; // lazy evaluation to avoid useless memory consumption
var pluginGetter = function() {
if (pluginInstance == null) pluginInstance = new plugin();
return pluginInstance; };
Object.defineProperty( Library.prototype, name,
{ get: pluginGetter, enumerable : true } );
} ;
A plugin is just standard javascript class:
function MyPlugin() {
this.pluginProperty1 = 'some value';
}
MyPlugin.prototype = {
method1 : function() { /* do things */} ,
method2 : function() { /* do other things */ }
};
Notice that with this scheme the plugin is a singleton, i.e. every instances of Library will return the same object when asked for the same plugin.
If you prefer once plugin instance per Library, just have the Library constructor hold the plugins instances. (maybe in a hidden property).
function Library() {
// the code allready here...
var pluginInstances = {};
Object.defineProperty(this, 'pluginInstances',
{ get : function() { return pluginInstances }, enumerable : false });
}
library.extend = function(name,plugin) {
var pluginGetter = function() {
if (! this.pluginInstances[name] ) this.pluginInstances[name] = new plugin();
return this.pluginInstances[name];
};
Object.defineProperty( Library.prototype, name,
{ get: pluginGetter, enumerable : true } );
} ;
syntax for the plugin and for the use remains the same.
Edit : for older Browser support, you can still use a function instead of a getter :
function Library() {
// the code allready here...
this.pluginInstances= {} ;
}
library.extend = function(name,plugin) {
Library.prototype[name] = function() {
if (! this.pluginInstances[name] ) this.pluginInstances[name] = new plugin();
return this.pluginInstances[name];
};
} ;
to use it you would do :
var myLibrary = new Library();
myLibrary.somePlugin().someMethod(arg1, arg2, ...);
Edit 2 : version with singleton plugins and with no getters is :
function Library() { /* same code */ }
library.extend = function(name,plugin) {
var pluginInstance = null; // lazy evaluation to avoid useless memory consumption
Library.prototype[name] = function() {
if (pluginInstance == null) pluginInstance = new plugin();
return pluginInstance; };
}
That's an interesting question. There was a blog post about prototype-liked development and the fact that a lot of people are avoiding it. I'll go with something like this:
var Library = function() {
var api;
var private = "some value";
var privateMethod = function() {
console.log(private);
}
var registerPlugin = function(name, plugin) {
api[name] = plugin.call(api);
}
var publicMethod = function() {
privateMethod();
}
return api = {
show: publicMethod,
plugin: registerPlugin
}
}
// usage of the library
var library = new Library();
library.show();
// registering a plugin
library.plugin("awesome", function() {
var api, library = this;
var pluginVar = "That's a plugin";
var pluginMethod = function() {
console.log(pluginVar);
library.show();
}
return api = {
gogo: pluginMethod
}
});
// calling a method of the plugin
library.awesome.gogo();
The library is just a function which has its own scope, its own private and public methods and exports an API. The plugin is actually another function with the same capabilities, but it is invoked with the library's API as a scope. So, all the public methods of the library are available and you are able to use them. And of course you keep the privacy of the plugin. I'll suggest to read about revealing module pattern. I personally use it alot. It really saves me a lot of problems.
P.S.
Here is a jsfiddle using the code above http://jsfiddle.net/XyTJF/
I want to create a global namespace for my application and in that namespace I want other namespaces:
E.g.
Dashboard.Ajax.Post()
Dashboard.RetrieveContent.RefreshSalespersonPerformanceContent();
I also want to place them in seperate files:
Ajax.js
RetrieveContent.js
However I have tried using this method, however it won't work because the same variable name is being used for the namespace in 2 seperate places. Can anyone offer an alternative?
Thanks.
You just need to make sure that you don't stomp on your namespace object if it's already been created. Something like this would work:
(function() {
// private vars can go in here
Dashboard = Dashboard || {};
Dashboard.Ajax = {
Post: function() {
...
}
};
})();
And the RetrieveContent file would be defined similarly.
Here is a very good article on various "Module Patterns" in JavaScript. There is a very nice little section on how you can augment modules, or namespaces and maintain a cross-file private state. That is to say, the code in separate files will be executed sequentially and properly augment the namespace after it is executed.
I have not explored this technique thoroughly so no promises... but here is the basic idea.
dashboard.js
(function(window){
var dashboard = (function () {
var my = {},
privateVariable = 1;
function privateMethod() {
// ...
}
my.moduleProperty = 1;
my.moduleMethod = function () {
// ...
};
return my;
}());
window.Dashboard = dashboard;
})(window);
dashboard.ajax.js
var dashboard = (function (my) {
var _private = my._private = my._private || {},
_seal = my._seal = my._seal || function () {
delete my._private;
delete my._seal;
delete my._unseal;
},
_unseal = my._unseal = my._unseal || function () {
my._private = _private;
my._seal = _seal;
my._unseal = _unseal;
};
// permanent access to _private, _seal, and _unseal
my.ajax = function(){
// ...
}
return my;
}(dashboard || {}));
dashboard.retrieveContent.js
var dashboard = (function (my) {
var _private = my._private = my._private || {},
_seal = my._seal = my._seal || function () {
delete my._private;
delete my._seal;
delete my._unseal;
},
_unseal = my._unseal = my._unseal || function () {
my._private = _private;
my._seal = _seal;
my._unseal = _unseal;
};
// permanent access to _private, _seal, and _unseal
my.retrieveContent = function(){
// ...
}
return my;
}(dashboard || {}));
The Yahoo Namespace function is exactly designed for this problem.
Added:
The source of the function is available. You can copy it into your own code if you want, change the root from YAHOO to something else, etc.
There are several libraries that already offer this sort of functionality if you want to use or examine a pre-baked (that is, a tested) solution.
YUI.attribute and YUI.base
dojo.mixin
underscore.extend
jQuery.extend
goog.provide and goog.object.extend
The simplest and most bug free one to get going with is probably jQuery.extend, with the deep argument set to true. (The reason I say it is bug free is not because I think that jQuery.extend suffers from less bugs than any of the other libraries -- but because it offers a clear option to deep copy attributes from the sender to the receiver -- which most of the other libraries explicitly do not provide. This will prevent many hard-to-diagnose bugs from cropping up in your program later because you used a shallow-copy extend and now have functions executing in contexts you weren't expecting them to be executing in. (If however you are cognizant of how you will be extending your base library while designing your methods, this should not be a problem.)
With the NS object created, you should just be able to add to it from where ever. Although you may want to try var NS = NS || {}; to ensure the NS object exists and isn't overwritten.
// NS is a global variable for a namespace for the app's code
var NS = NS || {};
NS.Obj = (function() {
// Private vars and methods always available to returned object via closure
var foo; // ...
// Methods in here are public
return {
method: function() {
}
};
}());
You could do something like this...
HTML page using namespaced library:
<html>
<head>
<title>javascript namespacing</title>
<script src="dashboard.js" type="text/javascript"></script>
<script src="ajax.js" type="text/javascript"></script>
<script src="retrieve_content.js" type="text/javascript"></script>
<script type="text/javascript">
alert(Dashboard.Ajax.Post());
alert(Dashboard.RetrieveContent.RefreshSalespersonPerformanceContent());
Dashboard.RetrieveContent.Settings.Timeout = 1500;
alert(Dashboard.RetrieveContent.Settings.Timeout);
</script>
</head>
<body>
whatever...
</body>
</html>
Dashboard.js:
(function(window, undefined){
var dashboard = {};
window.Dashboard = dashboard;
})(window);
Ajax.js:
(function(){
var ajax = {};
ajax.Post = function() { return "Posted!" };
window.Dashboard.Ajax = ajax
})();
Retrieve_Content.js:
(function(){
var retrieveContent = {};
retrieveContent.RefreshSalespersonPerformanceContent = function() {
return "content retrieved"
};
var _contentType;
var _timeout;
retrieveContent.Settings = {
"ContentType": function(contentType) { _contentType = contentType; },
"ContentType": function() { return _contentType; },
"Timeout": function(timeout) { _timeout = timeout; },
"Timeout": function() { return _timeout; }
};
window.Dashboard.RetrieveContent = retrieveContent;
})();
The Dashboard.js acts as the starting point for all namespaces under it. The rest are defined in their respective files. In the Retrieve_Content.js, I added some extra properties in there under Settings to give an idea of how to do that, if needed.
I believe the module pattern might be right up your alley. Here's a good article regarding different module patterns.
http://www.adequatelygood.com/2010/3/JavaScript-Module-Pattern-In-Depth
I highly recommend you use this technique:
https://github.com/mckoss/namespace
namespace.lookup('com.mydomain.mymodule').define(function (ns) {
var external = namespace.lookup('com.domain.external-module');
function myFunction() {
...
}
...
ns.extend({
'myFunction': myFunction,
...
});
});
I've been using this pattern for a couple of years; I wish more libraries would do the same thing; it's made it much easier for me to share code across my different projects as well.
i wrote this function to simplify creating namespaces. Mabey it will help you.
function ns(nsstr) {
var t = nsstr.split('.');
var obj = window[t[0]] = window[t[0]] || {};
for (var i = 1; i < t.length; i++) {
obj[t[i]] = obj[t[i]] || {};
obj = obj[t[i]];
}
}
ns('mynamespace.isawesome.andgreat.andstuff');
mynamespace.isawesome.andgreat.andstuff = 3;
console.log(mynamespace.isawesome.andgreat.andstuff);
bob.js can help in defining your namespaces (among others):
bob.ns.setNs('Dashboard.Ajax', {
Post: function () { /*...*/ }
});
bob.ns.setNs('Dashboard.RetrieveContent', {
RefreshSalespersonPerformanceContent: function () { /*...*/ }
});
Implementation:
namespace = function(packageName)
{
// Local variables.
var layers, layer, currentLayer, i;
// Split the given string into an array.
// Each element represents a namespace layer.
layers = packageName.split('.');
// If the top layer does not exist in the global namespace.
if (eval("typeof " + layers[0]) === 'undefined')
{
// Define the top layer in the global namesapce.
eval(layers[0] + " = {};");
}
// Assign the top layer to 'currentLayer'.
eval("currentLayer = " + layers[0] + ";");
for (i = 1; i < layers.length; ++i)
{
// A layer name.
layer = layers[i];
// If the layer does not exist under the current layer.
if (!(layer in currentLayer))
{
// Add the layer under the current layer.
currentLayer[layer] = {};
}
// Down to the next layer.
currentLayer = currentLayer[layer];
}
// Return the hash object that represents the last layer.
return currentLayer;
};
Result:
namespace('Dashboard.Ajax').Post = function() {
......
};
namespace('Dashboard.RetrieveContent').RefreshSalespersonPerformanceContent = function() {
......
};
Gist:
namespace.js