How to write simple, extensible, modular Javascript - javascript

I need a mechanism whereby people can extend my base code with their own modules - but I'm struggling to come-up with a simple mechanism to do that.
Example: a function called 'test' which users can extend. Each user module is loaded after the original - so each one needs to build on the last (the order they're loaded should not matter or can be controlled by naming)
I started to play with something like this
var test = function() { // the master function
console.log("1");
}
var ltest = test; // module 1
var test = function() {
ltest();
console.log("2");
}
var ltest2 = test; // module 2
var test = function() {
ltest2();
console.log("3");
}
Then, when 'test' is called, it will run everyone's code (assuming no-one forgot their callback!!)
That works, but it relies on each module declaring it's own, unique 'callback' variable (ltest, ltest2) - if someone uses the same variable, we'll get a 'call stack exceeded' as those variables are global in scope...
Can anyone suggest a cleverer/better system - or point me to some examples of the same thing?
There's loads of material on inheritance but I don't want to create new things which extend the old one - I just want to extend the old one!!
p.s. taking the anonymous function stuff from the module pattern - I got this
var test = function() {
console.log("1");
}
(function() {
var oldtest = test;
test = function() {
oldtest();
console.log("2");
}
}())
(function() {
var oldtest = test;
test = function() {
oldtest();
console.log("3");
}
}())
Which is probably the simplest solution to my question - but not necessarily the best system to use (as it's dependant on the author to remember to callback the code - a dodgy module would break everything)

The Module Pattern is what you need.
In particular the 'Augmentation' or 'Loose Augmentation' patterns :
var MODULE = (function (my) {
var old_moduleMethod = my.moduleMethod;
my.moduleMethod = function () {
// method override, has access to old through old_moduleMethod...
};
return my;
}(MODULE || {}));

You could make a function like this
function extendFunction(fn, pre, post) {
return function () {
var arg = arguments;
if (pre) arg = pre.apply(this, arg); // call pre with arguments
arg = fn.apply(this, arg); // call fn with return of pre
if (post) arg = post.apply(this, arg); // call post with return of fn
return arg;
};
}
then extend as follows
var test = function () { // the master function
console.log("1");
};
test = extendFunction(
test, // function to extend
null, // thing to do first
function() {console.log("2");} // thing to do after
);
test = extendFunction(
test,
null,
function() {console.log("3");}
);
test(); // 1, 2, 3
This is very different to the normal meaning of "extend" though, where you give new properties to Objects or set up prototype chains, and to "module" which normally involves wrapping all your code in a function expression so that you don't pollute the namespace.

Related

Synchrone issue readdir with singleton in Node.js

First app with Node.js, trying to make a file explore to get contents from them, within a singletoned class, but the order is not that i expected. Surely a knowledge missing from me, can you tell me why..
Singleton class :
var Singleton = (function()
{
var _instance = null;
return new function()
{
this.Instance = function()
{
if (_instance == null)
{
_instance = new Foo();
}
return _instance;
}
};
})();
The Foo class :
var Foo= function Foo()
{
this._filesDir= "./core/files/";
this._storedFiles = {};
this.method1();
console.log("call constructor");
};
Foo.prototype = {
method1: function()
{
console.log("call method1");
var that = this;
var c = 0;
fs.readdirSync(this._filesDir).forEach(function(fileName)
{
console.log("iterating file"+ c);
c++;
fs.readFile(that._filesDir + fileName, 'utf-8', function(err, content)
{
var clean_FileName = fileName.replace(".txt", "");
console.log( clean_fileName );
that._storedFiles[ clean_fileName ] = content;
});
});
},
method2: function( fileName )
{
console.log('call method2');
return ( fileName in this._storedFiles);
}
};
The calling :
console.log( Singleton.Instance().method2("myfile") );
In the directory, there is only this myfile.txt
But, the console displaying me that :
call method1
iterating file0
call constructor
call method2
false
GET /test 304 11ms
myfile
So my response is false and is this normal constructor called at the third position ? I need the class construct, store, and finally execute the method2(). What i'm doing bad ?
The root of your problem is that fs.readFile is asynchronous. method1 returns before you've read the contents of the file. A simple fix is to change it to fs.readFileSync.
The reason "call constructor" is third is because you invoke method1() first.
this.method1();
console.log("call constructor");
Everything in method1 gets run before console.log("call constructor") happens. You can simply swap the two if you want the order to be correct.
From a high-level, using synchronous calls (readdirSync, readFileSync) is usually a bad idea because they block Node from doing anything else while they're running. I would recommend studying callbacks, control flow, and the asynchronous nature of Node.js. There are lots of great tutorials out there.

Node.js double call to require()

//lib.js
var opt = 0
exports.set = function(arg) {
opt = arg
}
exports.prn = function() {
console.log(opt)
}
///prog.js
var lib = require('./lib')
var lib2 = require('./lib')
lib.set(222)
lib2.set(333)
lib.prn()
lib2.prn()
prog.js will output:
333
333
but I need it to output:
222
333
In ohter words, opt must be unique to variable lib and to variable lib2. How to achieve that?
That's because normally nodejs caches its modules which are got via require. You may use the following helper:
// RequireUncached.js
module.exports = function(module) {
delete require.cache[require.resolve(module)]
return require(module);
}
and the usage of the helper:
var requireUncached = require('RequireUncached.js');
requireUncached("./lib");
Have in mind that this approach is considered as bad practice and should not be used. I'll suggest to wrap your logic into a function, require the module and call the function. So, every time you get a new instance.
require will not load scripts multiple times, but always yield the same instance.
If you need different environments, make your module a constructor function that allows to be instantiated multiple times. Store opt on each object for that instead of in the (global) module scope.
// lib.js
module.exports = function constr() {
var opt = 0
this.set = function(arg) {
opt = arg
};
this.print = function() {
console.log(opt)
};
};
// prog.js
var lib = require('./lib'),
inst1 = new lib(),
inst2 = new lib();
/* or short:
var inst1 = new require('./lib')(),
inst2 = new require('./lib')(); */
inst1.set(222)
inst2.set(333)
inst1.print()
inst2.print()
The way the NodeJS module system works, the output is correct and your expectations contradict the design principle here.
Each module is loaded once and only once, and subsequent calls to require simply return the reference to the pre-existing module.
Maybe what you need to do is create a class you can create one or more instances of instead of using module-level globals.
Adding to Bergi's answer, You may also try it like
// prog.js
var lib = require('./lib')(),
lib2 = require('./lib')();
lib.set(222)
lib2.set(333)
lib.print()
lib2.print()
// lib.js
module.exports = function constr() {
var opt = 0
return { set : function(arg) {
opt = arg
},
print : function() {
console.log(opt)
}
}
};
Add this line as first line of your lib.js
delete require.cache[__filename]
now your module becomes in a separate namespace each time you require it.

Javascript mixins when using the module pattern

I've been using the module pattern for a while, but recently have started wanting to mix in functions and properties into them to increase code re-use. I've read some good resources on the subject, but still am a bit uncertain as to the best approach. Here is a module:
var myModule = function () {
var privateConfigVar = "Private!";
//"constructor"
function module() {}
module.publicMethod = function () {
console.log('public');
}
function privateMethod1() {
console.log('private');
}
return module;
}
And here is a mixin object:
var myMixin = function () {};
Mixin.prototype = {
mixinMethod1: function () {
console.log('mixin private 1');
},
mixinMethod2: function () {
console.log('mixin private 2');
}
};
Ideally, I'd like to mix-in some methods from other objects as private methods and some as public methods, so that I could call some "extend" function, with a param as "private"/"public". So, that
mixin(myModule, myMixin, "private");
makes the myMixin methods available within myModule by just calling mixinMethod1() and have correct scope, and:
mixin(myModule, myMixin, "public");
makes the myMixin methods available within myModule by calling module.mixinMethod1() and have correct scope
I've tried using a method that copies properties from one prototype to another, I've tried the underscore extend method to copy properties of the object from one to to the other, and various things in between. I think I'm a bit turned around regarding scope and prototypes at this point, and would love some direction as to how best to do mixins like this when using the module pattern. Note that it doesn't matter what the object myMixin looks like (whether adding functions to the prototype, or a module itself), I'm just trying to figure out some way to make it work.
Thank!
So that [some code] makes the myMixin methods available within myModule by just calling mixinMethod1() and have correct scope
That's impossible. You cannot modify a scope by calling a function, especially not from outside. See also Is it possible to import variables in JavaScript? for the design reasons of that.
So, what can you do?
From outside the module
Nothing to the private scope(s) of module functions. And you cannot use the private functions of the module, obviously. You can extend its prototype with methods (which is the most common), you can even decorate its constructor function. Within those, you can use your own private functions, either completely static ones or class-specific ones.
var myMixin = (function() {
// everything class-unspecific but mixin-local
var staticMixinVariables, …;
function globalPrivateFunction(){…}
function staticMethod(){…}
return function(mod) {
// everything class-specific
// also using the locals from above
mod.staticHelper = function() { staticMixinVariable … };
mod.prototype.mixinMethod1 = staticMethod;
mod.prototype.mixinMethod2 = function(){…};
…
};
})();
// Example:
myMixin(SomeClass)
From within the module
Using the mixin in the module code itself can allow for much greater flexibility.
var myMixin = (function() {
// everything class-unspecific but mixin-local
…
return {
publicHelper1: function(){…},
publicHelper2: function(){…},
decorateInstance: function(o) {
o.xy = …;
},
extendPrototype: function(proto) {
// everything class-specific
// also using the locals from above
proto.mixinMethod1 = staticMethod;
proto.mixinMethod2 = function(){…};
…
}
};
})();
With such an interface, it becomes easy to construct a class that is using this as a mixin (not inheriting from it):
var myClass = (function() {
function Constructor() {
myMixin.decorateInstance(this);
…
}
Constructor.prototype.method1 = function() { myMixin.publicHelper1() … };
Constructor.prototype.method2 = function() { … };
myMixin.extendPrototype(Constructor.prototype);
Constructor.myHelper = myMixin.publicHelper2; // re-export explicitly
return Constructor;
})();
However, the mixin will never have access to the private class variables, nor can it present a private, class-specific API. Still, we can use dependency injection to provide that access explicitly (and having a mixin factory in effect):
var myClass = (function() {
var … // private class functions and variables
var mixer = myMixin(privateClassHelper,
privateClassVariable,
function setPrivateVar(x) {…},
… );
var myHelper = mixer.customHelper, … // local "aliases"
function Constructor(localX) {
mixer.decorateInstance(this, localX);
…
}
… // further using the class-specific private mixer
return Constructor;
})();
Not all techniques shown above need to be used in every mixin, just choose the ones you need. Not all possible techniques are shown in the above examples, also :-) The mixin pattern can be applied onto a plain module or inside its declaration as well, the above examples have only shown classes with prototypes.
For a few good examples, and a theoretical distinction between (stateless) Traits, (stateful) Mixins and their "privileged" counterparts, have a look at this presentation.
The with keyword can be very usefull to define a scope, but it has also some drawbacks (it is by the way forbidden in strict mode).
Using the with keyword, you can define a private variable privateScope within the body of your module, that would contain all your provate methods :
var myModule = function () {
var privateConfigVar = "Private!";
var privateScope = {};
//"constructor"
function module() {}
var proto = module.prototype;//avoids multiple attribute lookup
//Let's re-define you example' private method, but with a new strategy
privateScope['privateMethod1'] = function() {
console.log('private');
}
proto.publicMethod = function () {
with(privateScope){
//this call should work
privateMethod1();
}
console.log('public');
}
proto.publicMethod2=function(name,fn){
with(privateScope){
//this will be defined later by a Mixin
otherPrivateMethod();
}
console.log('public2');
}
proto.definePrivateFunction=function(name,fn){
privateScope[name] = fn;
}
return module;
}
Your mixin will use the definePrivateFunction we just defined to add private methods to the private scope :
//An example mixin implementation
function Mixin(source,target,flag){
if(flag==="private"){
for(var currentMethodName in source){
target.definePrivateFunction(currentMethodName,source[currentMethod])
}
}else{
for(var currentMethodName in source){
target[currentMethodName]=source[currentMethod];
}
}
}
The following code should work fine:
var test = myModule();
var testInstance = new test();
testInstance.publicMethod();// will call the private method defined internally
Mixin({
otherPrivateMethod:function(){
console.log("other Prvate Method called")
}
},test.prototype,"private");
testInstance.publicMethod2();// will call the private method defined by the mixin
Ideally, I'd like to mix-in some methods from other objects as private methods and some as public methods, so that I could call some "extend" function, with a param as "private"/"public". ...
As it already has been mentioned, there is no way of achieving exactly this goal.
So, that ... makes the myMixin methods available within myModule by just calling mixinMethod1() and have correct scope, and: ... makes the myMixin methods available within myModule by calling module.mixinMethod1() and have correct scope.
And referring to scope ... this is a closed address space created by functions.
Except for closures, scope only is available during a function's runtime
within this function's body. It never ever can be manipulated/spoofed.
The term one is looking for is context. JavaScript, being in many ways highly
dynamic, is build upon late binding (the object/target/context a method is called
on gets evaluated/looked up at runtime) and two kinds of delegation.
Context gets delegated either automatically by "walking the prototype chain"
or explicitly by one of both call methods which every function object does provide
- either call or apply.
Thus JavaScript already at language core level does offer a function based
Mixin pattern that is mightier than any of the available extend(s) or mixin
implementations for it provides delegation for free and is able of passing
around state which almost every of the blamed helpers does lack unless there
was effort of implementing this feature again in a rather roundabout fashion
(or ass-backwards to put it bluntly).
Bergi for his explanation already earned the bounties.
Within his answer's last paragraph there is a link to resources of mine that
already got outdated 3 month after giving the referred talk. Due of not having
enough reputation points, I'm not able to comment his answer directly. For this
I'll take the chance pointing now to the latest state of my personal research and
understanding of »The many talents of JavaScript for generalizing Role Oriented Programming approaches like Traits and Mixins«
Back again answering the OP's question.
I'm going to change the two first given code examples from the assumed module pattern
and the rather exemplarily provided mixin code base towards a plain constructor function
and what I'm meanwhile tempted to call a "proxified" and/or "bicontextual" mixin in order
to boil down the mechanics of delegating two different target/context objects at once.
Thus demonstrating a pure function based mixin pattern that might come closest to what
the OP tries to achieve.
var MyBicontextualMixin = function (localProxy) {
localProxy.proxifiedAccessible = function () {
console.log("proxified accessible.");
};
this.publiclyAccessible = function () {
console.log("publicly accessible.");
};
};
var MyConstructor = function () {
var localProxy = {};
MyBicontextualMixin.call(this, localProxy);
var locallyAccessible = localProxy.proxifiedAccessible;
// call 'em
locallyAccessible(); // "proxified accessible."
this.publiclyAccessible(); // "publicly accessible."
};
(new MyConstructor);
// will log:
//
// proxified accessible.
// publicly accessible.
This special pattern also is the underlying base for composing pure
function based Traits that rely on conflict resolution functionality
provided by "proxified" Mixins that won't expose this functionality
into public.
And for not ending up that theoretical there will be a "real world example",
composing a Queue module out of various reusable mixins that entirely
worship the approach of DRY. It also should answer the OP's question about
how to achieve encapsulation and exposition build only upon the module
pattern and function based mixin composition.
var Enumerable_first_last_item = (function (global) {
var
parseFloat = global.parseFloat,
math_floor = global.Math.floor,
// shared code.
first = function () {
return this[0];
},
last = function () {
return this[this.length - 1];
},
item = function (idx) {
return this[math_floor(parseFloat(idx, 10))];
}
;
return function () { // [Enumerable_first_last_item] Mixin.
var enumerable = this;
enumerable.first = first;
enumerable.last = last;
enumerable.item = item;
};
}(window || this));
var Enumerable_first_last_item_proxified = function (list) {
Enumerable_first_last_item.call(list);
// implementing the proxified / bicontextual [Enumerable_first_last_item] Mixin.
var enumerable = this;
enumerable.first = function () {
return list.first();
};
enumerable.last = function () {
return list.last();
};
enumerable.item = function (idx) {
return list.item(idx);
};
};
var Allocable = (function (Array) {
var
array_from = ((typeof Array.from == "function") && Array.from) || (function (array_prototype_slice) {
return function (listType) {
return array_prototype_slice.call(listType);
};
}(Array.prototype.slice))
;
return function (list) { // proxified / bicontextual [Allocable] Mixin.
var
allocable = this
;
allocable.valueOf = allocable.toArray = function () {
return array_from(list);
};
allocable.toString = function () {
return ("" + list);
};
allocable.size = function () {
return list.length;
};
Enumerable_first_last_item_proxified.call(allocable, list);
};
}(Array));
var Queue = (function () { // [Queue] Module.
var
onEnqueue = function (queue, type) {
//queue.dispatchEvent({type: "enqueue", item: type});
},
onDequeue = function (queue, type) {
//queue.dispatchEvent({type: "dequeue", item: type});
}/*,
onEmpty = function (queue) {
//queue.dispatchEvent({type: "empty"});
}*/,
onEmpty = function (queue) {
//queue.dispatchEvent("empty");
},
Queue = function () { // [Queue] Constructor.
var
queue = this,
list = []
;
queue.enqueue = function (type) {
list.push(type);
onEnqueue(queue, type);
return type;
};
queue.dequeue = function () {
var type = list.shift();
onDequeue(queue, type);
(list.length || onEmpty(queue));
return type;
};
//Observable.call(queue); // applying the [Observable] Mixin.
Allocable.call(queue, list); // applying the bicontextual [Allocable] Mixin.
},
isQueue = function (type) {
return !!(type && (type instanceof Queue));
},
createQueue = function () { // [Queue] Factory.
return (new Queue);
}
;
return { // [Queue] Module.
isQueue : isQueue,
create : createQueue
};
}());
var q = Queue.create();
//q.addEventListener("enqueue", function (evt) {/* ... */});
//q.addEventListener("dequeue", function (evt) {/* ... */});
//q.addEventListener("empty", function (evt) {/* ... */});
console.log("q : ", q); // { .., .., .., }
console.log("q.size() : ", q.size()); // 0
console.log("q.valueOf() : ", q.valueOf()); // []
"the quick brown fox jumped over the lazy dog".split(/\s+/).forEach(function (elm/*, idx, arr*/) {
console.log("q.enqueue(\"" + elm + "\")", q.enqueue(elm));
});
console.log("q.size() : ", q.size()); // 9
console.log("q.toArray() : ", q.toArray()); // [ .., .., .., ]
console.log("q.first() : ", q.first()); // "the"
console.log("q.last() : ", q.last()); // "dog"
console.log("q.item(2) : ", q.item(2)); // "brown"
console.log("q.item(5) : ", q.item(5)); // "over"
console.log("q.dequeue()", q.dequeue()); // "the"
console.log("q.dequeue()", q.dequeue()); // "quick"
console.log("q.dequeue()", q.dequeue()); // "brown"
console.log("q.dequeue()", q.dequeue()); // "fox"
console.log("q.dequeue()", q.dequeue()); // "jumped"
console.log("q.size() : ", q.size()); // 4
console.log("q.toArray() : ", q.toArray()); // [ .., .., .., ]
console.log("q.first() : ", q.first()); // "over"
console.log("q.last() : ", q.last()); // "dog"
console.log("q.item(2) : ", q.item(2)); // "lazy"
console.log("q.item(5) : ", q.item(5)); // undefined
.as-console-wrapper { max-height: 100%!important; top: 0; }

Difference between javascript constructors

I'm having a bit of confusion over which (if any) of these javascript initializes to use, as from what I can tel, they all do the same thing. Are there cases in which I should use one or he other? Mt project requires me to encapsulate several functions and namespaces in a single object so that we don't clutter te global scope with variable names, put I'm especially unclear as to the difference between +function(){}(); and (function())();
Any help would be greatly appreciated. Keep in mind the end goal is for all of our function to be encapsulated in a single namespace. IE MyCompany.function, MyCompany.Namepsace.Function ...
<script>
var Ford = {};
+function() {
Ford.start = function() {
console.log("Ford just started");
};
}();
Ford.start();
</script>
<script>
var Honda = {};
(function() {
Honda.start = function() {
console.log("Honda just srtarted");
};
})();
Honda.start();
</script>
<script>
var Toyota = function() {
return {
start: function() {
console.log("Toyota just strted");
}
};
}
var car = new Toyota();
car.start();
</script>
Javascript functions are only invoked as constructors when the new keyword is used. new creates a new object instance, inherits from the constructor's prototype, and makes the context of the this keyword the newly created object instance. So if you're not using the prototype to inherit properties or the keyword this to create new properties in the constructor--you don't really need to use a constructor.
I think what you're looking for is the module pattern which is implemented using immediately invoked functions. Both of your first two examples use immediately invoked functions. I believe the style (function(){}()); is preferred to +function(){}();. Both the surrounding () and the + cause the javascript parser to expect a function expression rather than a function declaration and this allows the trailing () to invoke the function. Besides being somewhat less readable + may also alter the return value of the function.
I think you want some variation on this (copied from link):
var MODULE = (function () {
var module = {};
var privateVariable = 1;
function privateMethod() {
// ...
}
module.moduleProperty = 1;
module.moduleMethod = function () {
// ...
};
return module;
}());
While all of that is valid JS, you're making assumptions that lead me to believe you may not understand how JS works. The first two example are not constructors in the usual sense at all...they're sort of module pattern, but not even. you could condense it to
var Honda = {};
Honda.start = function() { console.log("Honda just started"); }
and forget the self-executing function. What is shown above is similar to what you'd think of as a static function in other languages:
public class Honda {
public static void start() { Console.WriteLine("Honda Just Started"); }
}
If you wanted an instance function, you need to either attach to the prototype, or to the this keyword in the constructor:
Prototype:
var Honda = function() {}
Honda.prototype.start = function() { console.log("Honda just started"); }
This:
var Honda = function() {
this.start = function(){
console.log("Honda just started");
}
}
Your final example, with Toyota indicates you have a fundamental misunderstanding of how function constructors work. Your object has been discarded, and in return you get some object literal which could have been written:
var Toyota = {
start: function(){
console.log("Toyota just started");
}
}
What you (probably) intend is the "this" pattern I explained on the Honda example above:
var Toyota = function() {
this.start = function() {
console.log("Toyota just started");
}
}
Finally, when writing "namespaced" libraries of functions, the module pattern may be your best friend. This lets you maintain private state without the need for a formal constructor/prototypes, etc:
var MyCompany = {};
//car module
(function(ns){
var module,
_isStarted;
function start() { console.log('start'); _isStarted = true; }
function stop() { console.log('stop'); _isStarted = false; }
function isStarted() { return _isStarted; }
module = {
start: start,
stop: stop,
isStarted: isStarted
}
ns.CarModule = module;
}(MyCompany));
//use:
MyCompany.CarModule.start();
if(MyCompany.CarModule.isStarted()) { MyCompany.CarModule.stop(); }

Javascript Sandbox Pattern example implementation

On Page 101 of Stoyan Stefanov's great book "JavaScript Patterns" he explains the sandbox pattern.
I liked his book much but I really missed some real life examples here and then to better understand what he talks about.
Like the sandbox pattern!
I'm looking for a real life working implementation, like a copy&paste starting point, just a simple example that will work to fully understand it.
Is there any?
I've simplified Stoyan's example in an attempt to make it easier to understand what's going on. I've also commented it more thoroughly.
/*First define the modules of the sandbox. These will be defined
as properties on the constructor function because this is a
convenient place to keep them.*/
Sandbox.modules = {};
Sandbox.modules.returnNumbers = function(MYAPP) {
MYAPP.return100 = function() {return 100;};
};
Sandbox.modules.returnLetters = function(MYAPP) {
MYAPP.returnABC = function() {return "ABC";};
};
function Sandbox() {
/* Because Sandbox is a constructor, an new object is automatically
created. Because we're in the constructor, we refer to this new object
as 'this'.
A constructor would typically be used as part of an assignment, e.g.
myObject = new Sandbox().
However, it's also legitimate javascript to use a constructor without
the assignment by just writing new Sandbox() with no assignment. The
constructor does return an object, it's just that it doesn't get
assigned to anything so is discarded.
We're going to add functionality (methods) to the 'this' object, but
rather than returning it, we will pass it to the callback function, so
the methods can be used immediately.
*/
var args = Array.prototype.slice.call(arguments); //Put the arguments
//of the call to the Sandbox constructor in an array called args.
var callback = args.pop(); //The last argument is the callback
var requiredmodules = args; //The remaining arguments are the require
// modules
//For each of the modules in 'requiredmodules', add the module's
//methods to 'this'
for (i=0; i< requiredmodules.length; i++) {
Sandbox.modules[requiredmodules[i]](this);
}
//'this' now has methods returnNumbers and returnLetters
//Call the callback. In the example below, 'this' will be called
//MYAPP, which within the callback will have all the methods from
//the required modules.
callback(this);
}
//Finally here is an example of usage
new Sandbox('returnNumbers', 'returnLetters', function (MYAPP) {
console.log(MYAPP.return100());
console.log(MYAPP.returnABC());
});
Stoyan Stefanov mentions in the same chapter that YUI version 3 implements the Sandbox pattern. The YUI add method (API) registers modules and the use method (API) loads the specified ones in the sandbox instance. There are links to the source js file in the API documentation.
Virtually all YUI code examples use this pattern to work with the YUI library. Defining a module is rarely needed - YUI has many core ones and there is a page for custom modules added by the community.
So I tried and came up with this solution:
function Sandbox() {
// turning arguments into an array
var args = Array.prototype.slice.call(arguments),
// the last argument is the callback
callback = args.pop(),
// modules can be passed as an array or as individual parameters
modules = (args[0] && "string" === typeof args[0]) ? args : args[0],
i;
// make sure the function is called
// as a constructor
if (!(this instanceof Sandbox)) {
return new Sandbox(modules, callback);
}
// add properties to 'this' as needed:
this.a = 1;
this.b = 2;
// now add modules to the core 'this' object
// no modules or "*" both mean "use all modules"
if (!modules || '*' === modules) {
modules = [];
for (i in Sandbox.modules) {
if (Sandbox.modules.hasOwnProperty(i)) {
modules.push(i);
}
}
}
// initialize the required modules
for (i = 0; i < modules.length; i += 1) {
Sandbox.modules[modules[i]](this);
}
// call the callback
callback(this);
// any prototype properties as needed
Sandbox.prototype = {
name: "Sandbox",
version: "1.0",
getName: function() {
return this.name;
}
}
};
Sandbox.modules = {};
Sandbox.modules.color = function (box) {
// private
var initialColor = $('#main').css('color');
// set a red color
box.setMainRed = function() {
$('#main').css('color','red');
return false;
},
// get the current color
box.getInitialColor = function () {
return initialColor;
};
}
// another module
Sandbox.modules.style = function (box) {
// set a red color
box.setStyle = function() {
$('#main').css('font-style','italic');
return false;
};
}
// page ready
$.ready(
Sandbox(['color', 'style'], function (box) {
console.log(box);
box.setMainRed();
box.setStyle();
console.log('try access initialColor: ', box.initialColor);
console.log('get initial color: ', box.getInitialColor());
})
);
But I am really unsure weather this is what I should be doing.
Especially adding the "modules" is somewhat confusing. Also earlier in the book he uses the namespace-pattern for this task, but not here. Why? Can't you do it here too?
But I failed to combine these two patterns.
Namespace pattern example inspired by the book:
var APP = APP || {};
// namespace function
APP.namespace = function (nsString) {
var parts = nsString.split('.'),
parent = APP,
i;
// strip redundant leading global
if ("APP" === parts[0]) {
parts = parts.slice(1);
}
for (i = 0; i < parts.length; i += 1) {
// create a property if it doesn't exist
if ("undefined" === typeof parent[parts[i]]) {
parent[parts[i]] = {};
}
parent = parent[parts[i]];
}
return parent;
}
// constructors
APP.namespace('modules.Color');
// immediate function
APP.modules.Color = (function () {
var currentColor = $('#main').css('color'),
// set a red color
setMainRed = function() {
$('#main').css('color','red');
return false;
},
// get the current color
getCurrentColor = function () {
return currentColor;
};
// revealing module pattern
return {
setMainRed: setMainRed,
getCurrentColor: getCurrentColor
};
}());
var doSomething = function () {
var color = APP.modules.Color;
color.setMainRed();
console.log(color.currentColor);
console.log(color.getCurrentColor());
return false;
}
// page ready
$.ready(
doSomething()
);
Here is example with detailed comments:
(function(){
/* function constructor */
function Sandbox(){
//Change arguments to array, as you know 'arguments' are not a true JS array
//Array.prototype.slice will provide shallow copy of 'arguments'
var args = Array.prototype.slice.call(arguments),
//remove last element from array and return it to caller
//our last argument is callback
callback = args.pop(),
//We can pass modules as strings or as array
//if first element is a string, take all arguemnts
//otherwise take one element (array)
modules = (args[0] && typeof args[0] === "string") ? args : args[0],
modulesLength = modules.length,
i;
//handle calling function constructor without 'new' keyword
if(!(this instanceof Sandbox)){
//Invoke me again!
return new Sandbox(modules, callback);
}
//we can add properties to 'this'
this.someProp = "Initialized property";
//Initialize all required modules
for(i = 0; i < modulesLength ; i++){
//pass reference to 'this' for each required module and invoke it
//'this' is poiting to new object which was created
//after calling new Sandbox()
Sandbox.modules[modules[i]](this);
}
//Invoke callback and pass 'this'
//now 'this' cotains all methods and properties
//attached in modules functions
callback(this);
};
//We can optionally create Sandbox methods
Sandbox.prototype = {
version: "1.0.1",
createdAt: new Date()
};
/* function as a first class object - saving all modules*/
Sandbox.modules = {};
/*Create $http,$scope and $ajax modules */
/*We need box object to add new functionality*/
/*We are creating new methods by attatching them to box obect*/
/*box is a reference to 'this' called as initializator from function constructor*/
Sandbox.modules.$http = function(box){
box.get = function(){
console.log("$http.get");
};
box.post = function(){
console.log("$http.post");
};
box.prop = "I'm $http property";
};
Sandbox.modules.$scope = function(box){
box.inject = function(param1, param2){
console.log("$scope.inject: " + param1 + " " + param2);
};
box.destroy = function(o){
console.log("$scope.destroy: " + o + " has been destroyed!");
};
};
Sandbox.modules.$ajax = function(box){
box.call = function(){
console.log("$ajax.call");
};
};
//Sandbox without calling 'new' was handled in function constructor
//We are requesting for 2 modules: $scope and $http
//callback function is our new playground
//box object has $scope and $http methods and properties inside, we are ready to go!
Sandbox(["$scope", '$http'], function(box){
console.log(box); //contains methods from $scope and $http
console.log(box.inject("John", "Doe"));
console.log(box.post());
//we can event nest our playgrounds
Sandbox(["$ajax"], function(box){
console.log(box); //contains only $ajax methods and properties
console.log(box.call());
//we can't invoke $scope or $http functions here
});
//we can't invoke $ajax functions here
});
})();
Link to JSFiddle: http://jsfiddle.net/Lodse4hj/

Categories

Resources