Building a plugin architecture - javascript

I'm trying to build a simple plugin architecture that allows for a nice simple API in use. Consider the following code:
//the core library namespace
var testObj = {};
//constuctor
testObj = function() {
};
//The plugin
testObj.prototype.test = (function(){
var i = 0;
var init = function() {
alert('initialized');
}
return {
init: init,
render: function(){
return i;
}
};
}());
var p = new testObj();
//p.test.init();
p.test();
Working fiddle here: http://jsfiddle.net/8LwRL/
I want users to be able to call the init method by doing p.test(). Currently this doesn't work and anyone using the plugin would have to call p.test.init(). Is there any way that I can abstract that away so the user can call the prototype method by name and have the init run automatically?

As I already mentioned in my comment, if you want to make p.test callable, you have to return a function from the IIFE, not a (simple) object. The most straightforward approach would be to return init itself. Your code could then look like
testObj.prototype.test = (function(){
var i = 0;
var init = function() {
alert('initialized');
};
init.render = function(){
return i;
};
return init;
}());
It appears a bit odd (to me) but it would do what you want.

My corrected version:
//constuctor
var testObj = function(options) {
this.init(options);
};
//I prefer to use jQuery.extend() to extend prototype. Its look better
$.extend(testObj.prototype,{
options: {},
init: function (options) {
this.options = options;
console.log('Name on init ' + options.name );
this.options.name = 'Peter';
},
test: function () {
console.log('name on test: ' + this.options.name);
}
})
var p = new testObj({name: 'John'});
p.test();
jsfiddle

Related

Extending existing Object to add additional JavaScript Prototype methods

Let's say I have a function like the one below that creates a very simple micro-library:
var microLib = function(selector) {
var el;
var selectorEngine = function(selector)
{
var selector_value = selector.slice(1);
return document.getElementById(selector_value);
};
el = selectorEngine(selector);
Element.prototype.func_1 = function(){
return 'first';
};
Element.prototype.func_2 = function(){
return 'second';
};
return el;
};
window._$ = microLib;
This script will allow me to write code like this:
var elem = _$("#div_with_id"); //some element on my web page
console.log(elem.func_2()); //outputs 'second' in the console
So now, I'm looking for a way to extend _$ in a supplementary piece of code to add additional Element.prototype methods, which will allow me to write:
console.log(elem.func_3()); //to output 'third' in the console
The reason I need to do this is because this extension needs to take place in another JavaScript file, otherwise I would I have just added another method and be done with it.
How can I do this?
Here's an example of the approach that I am suggesting: http://jsfiddle.net/rbxssmx8/.
JS:
var toArray = Function.prototype.call.bind(Array.prototype.slice);
var qAll = document.querySelectorAll.bind(document);
var _$ = (function() {
function dom(selector) {
if(!(this instanceof dom)) {
return new dom(selector);
}
this.elements = toArray(qAll(selector));
}
dom.prototype.iterate = function(func) {
this.elements.forEach(func);
return this;
};
dom.prototype.addClass = function() {
var klasses = arguments;
return this.iterate(function(element) {
element.classList.add.apply(element.classList, klasses);
});
};
dom.extend = function(name, func) {
this.prototype[name] = func;
};
dom.ready = function(func) {
document.addEventListener("DOMContentLoaded", func);
};
return dom;
})();
_$.extend("removeClass", function() {
var klasses = arguments;
return this.iterate(function(element) {
element.classList.remove.apply(element.classList, klasses);
});
});
_$("div").addClass("gray");
var $el = _$("div:last-of-type");
$el.removeClass("gray");
So I read this post on What's wrong with extending the DOM and the alternative suggested by the author was to use Object Wrappers. A quick search on that led me to this post on SO: Using object wrappers to extend the JavaScripts DOM?
Coupled with some insight from #DRD's answer, I updated my code:
(function() {
var microLib = function (selector){
return new Dom(selector);
};
function Dom(selector)
{
var selector_value = selector.slice(1);
this.element = document.getElementById(selector_value);
}
Dom.prototype.func_1 = function(){
return 'first';
};
Dom.prototype.func_2 = function(){
return 'second';
};
microLib.extend = function(name, func){
Dom.prototype[name] = func;
};
window._$ = microLib;
})();
And then whenever you want to extend and add another function, do this afterwards:
_$.extend('func_3', function(){ //this is inline with my earlier question
return 'third';
});
Works like a charm! And definitely the safer option compared to extending Element.prototype.

Javascript module pattern, nested functions, and sub modules

I am trying to wrap my head around javascript modules, but I'm unsure how to split up a module into further sub modules. I have read that nested functions are not really a great idea, due to performance, so how do I break up a function in a module? For example, lets say I have the following module:
var Editor = {};
Editor.build = (function () {
var x = 100;
return {
bigFunction: function () {
// This is where I need to define a couple smaller functions
// should I create a new module for bigFunction? If so, should it be nested in Editor.build somehow?
}
};
})();
bigFunction is only related to Editor.build. Should I attach the smaller functions that make up bigFunction to the prototype bigFunction object? I'm not even sure if that would make sense.
var Editor = {};
Editor.build = (function () {
var x = 100;
return {
bigFunction: function () {
bigFunction.smallFunction();
bigFunction.prototype.smallFunction = function(){ /*do something */ };
// not sure if this even makes sense
}
};
})();
Can someone please throw me in the right direction here? There is so much misleading information online, and would just like a definite guide on how to deal with this sort of modularization.
Thank you.
Here is a snippet I use to make names for an input:
var dynamicCounter = 0;
//custom dropdown names
var createContainerNames = function () {
function Names() {
this.id = "Tasks_" + dynamicCounter + "__ContainerId";
this.name = "Tasks[" + dynamicCounter + "].ContainerId";
this.parent = "task" + dynamicCounter + "Container";
}
Names.prototype = { constructor: Names };
return function () { return new Names(); };
} ();
And then I use it:
var createdNames = createContainerNames();
var createdId = createdNames.id;
dynamicCounter++;
var differentNames = createContainerNames();
var differentId = differentNames.id;
Another approach would be to do this:
var NameModule = function(){
//"private" namemodule variables
var priv1 = "Hello";
//"private namemodule methods
function privMethod1(){
//TODO: implement
}
//"public namemodule variables
var pub1 = "Welcome";
//"public" namemodule methods
function PubMethod(){
//TODO: pub
}
return {
pub1 : pub1,
PubMethod: PubMethod
};
and then to use it
var myPubMethod = new NameModule();
myPubMethod.PubMethod();
var pubVar = myPubMethod.pub1;
EDIT
You could also take this approach:
var mod = function(){
this.modArray = [];
};
mod.prototype = {
//private variables
modId: null,
//public method
AddToArray: function (obj) {
this.modArray.push(obj);
}
}

JS turning a function into an object without using "return" in the function expression

i have seen in a framework (came across it once, and never again) where the developer defines a module like this:
core.module.define('module_name',function(){
//module tasks up here
this.init = function(){
//stuff done when module is initialized
}
});
since i never saw the framework again, i tried to build my own version of it and copying most of it's aspects - especially how the code looked like. i tried to do it, but i can't seem to call the module's init() because the callback is still a function and not an object. that's why i added return this
//my version
mycore.module.define('module_name',function(){
//module tasks up here
this.init = function(){
//stuff done when module is initialized
}
//i don't remember seeing this:
return this;
});
in mycore, i call the module this way (with the return this in the module definition):
var moduleDefinition = modules[moduleName].definition; //the callback
var module = moduleDefinition();
module.init();
how do i turn the callback function into an object but preserve the way it is defined (without the return this in the definition of the callback)?
you have to use:
var module = new moduleDefinition();
and then you're going to get an object.
Oh, and maybe you want to declare init as this:
this.init = function() {
Cheers.
How about something like this (I can only assume what mycore looks like):
mycore = {
module: {
definitions: {},
define: function(name, Module) {
this.definitions[name] = new Module();
this.definitions[name].init();
}
}
};
mycore.module.define('module_name', function () {
// module tasks up here
this.init = function () {
// init tasks here
console.log('init has been called');
};
});
I don't know what framework you're using or what requirements it places on you, but Javascript alone doesn't require a function to return anything, even a function that defines an object. For example:
function car(color) {
this.myColor = color;
this.getColor = function() {
return this.myColor;
}
//note: no return from this function
}
var redCar = new car('red');
var blueCar = new car('blue');
alert(redCar.getColor()); //alerts "red"
alert(blueCar.getColor()); //alerts "blue"
One more alternative http://jsfiddle.net/pWryb/
function module(core){this.core = core;}
function Core(){
this.module = new module(this);
}
Core.prototype.modules = {};
module.prototype.define = function(name, func){
this.core.modules[name] = new func();
this.core.modules[name].name = name;
this.core.modules[name].init();
// or
return this.core.modules[name];
}
var myCore = new Core();
var myModule = myCore.module.define('messageMaker', function(){
this.init = function(){
console.log("initializing " + this.name);
}
})
myModule.init();

JavaScript inheritance without the 'new' keyword

I'm used to using this pattern all over my code, and I like it:
var UserWidget = (function(){
var url = "/users",
tmpl = "#users li", $tmpl;
function load() {
$tmpl = $(tmpl);
$.getJSON(url, function(json){
$.each(json, function(i, v) {
appendUser(v);
});
});
}
...
return {
load: load
};
})();
However, I have many "widget" objects. "ProfileWidget", "PlayerWidget" etc etc. and there's certain actions that each widget share. So ideally, if we're thinking object-orientally, I want each widget object to inherit some methods from a main "Widget" class.
How can I do this without changing this lovely pattern I've been using?
To be more clear I'd like to be able to do something like this:
var Widget = (function() {
function init() {
console.log("wow yeah");
}
})();
// have UserWidget inherit somehow the Widget stuff
var UserWidget = (function() { ...
UserWidget.init(); // -> "wow yeah"
Keep in mind these solutions are not what I'd typically reccomend and they are just to satisfy the question.
What about closing over everything so that its accessible from your "sub classes" (demo)
var Widget = (function () {
var init = function () {
console.log("wow yeah");
};
var User = (function () {
var load = function () {
init();
};
return {
'load': load
};
} ());
return { 'User': User };
} ());
// Usage: This loads a user and calls init on the "base"
Widget.User.load();
Another way (demo) that you might like is to just use proper inheritance, but within the closure and then return one and only one instance of that new function. This way lets you keep User and whatever else an object
// Closing around widget is completely unneccesarry, but
// done here in case you want closures and in case you
// dont want another instance of widget
var Widget = (function () {
// definition that we'll end up assigning to Widget
function widget() {
console.log("base ctor");
}
// sample method
widget.prototype.init = function () {
console.log("wow yeah");
};
// put widget in Widget
return widget;
} ());
var User = (function () {
function user() { }
user.prototype = new Widget();
// TODO: put your User methods into user.prototype
return new user();
} ());
var Player = (function () {
function player() { }
player.prototype = new Widget();
// TODO: put your Player methods into player.prototype
return new player();
} ());
User.init();
Player.init();
I decided to use Crockford's object:
// function from Douglas Crockford, comments from me
function object(o) {
// define a new function
function F() {}
// set the prototype to be the object we want to inherit
F.prototype = o;
// return a new instance of that function, copying the prototype and allowing us to change it without worrying about modifying the initial object
return new F();
}
// Usage:
var Widget = (function() {
function init() {
console.log("wow yeah");
}
return {
init: init
};
})();
var UserWidget = (function() {
var self = object(Widget); // inherit Widget
function priv() {}
self.pub = "boom";
...
return self;
})();
UserWidget.init() // -> "wow yeah"
This works great for me and I like it!
You could use Object.create(obj), which I believe is what you're looking for.
Without using new, you'll have to use the __proto__ property rather than prototype, so this won't work in all browsers.
var Widget = {
init: function () {
console.log("wow yeah");
}
};
var UserWidget = (function(){
var url = "/users",
tmpl = "#users li",
$tmpl;
function load() {
$tmpl = $(tmpl);
$.getJSON(url, function(json){
$.each(json, function(i, v) {
appendUser(v);
});
});
}
return {
load: load
};
})();
UserWidget.__proto__ = Widget;
UserWidget.init();
Demo: http://jsfiddle.net/mattball/4Xfng/
Here's a simple example of prototyping in JS... For more detail on this topic read "JavaScript: The Good Parts"
// widget definition
var Widget = {
init: function () {
alert('wow yeah!');
}
};
// user widget definition
var UserWidget = function () { };
UserWidget.prototype = Widget;
UserWidget.prototype.load = function () { alert('your code goes here'); }
// user widget instance
var uw = new UserWidget();
uw.init(); // wow yeah!
uw.load(); // your code goes here
Hope this helps!

JavaScript Namespace

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

Categories

Resources