Tuesday, March 25, 2014

StarShine factory composition

It's become a habit of mine to discover and re-create my libraries with a twist using a slightly different technique. The flavor of this week is starshine.

~function(window, define, module, Array, Object){
'use strict';
var slice = Array.slice, create = Object.create;
function mixIn(target, ref) {
for (var key in ref) {
if (ref.hasOwnProperty(key)) {
target[key] = ref[key];
}
}
return target;
}
function StarShine() {
var enclosed = slice.call(arguments),
proto = {}, type = "anonymous";
function factory(options) {
var obj = create(proto), item, i = 0, _len = enclosed.length;
options = options || {};
obj._type = type;
for(;i<_len;) {
item = enclosed[i++]
obj = mixIn(obj,typeof item==='function'?item.call(obj, options)||{}:item);
}
return obj;
}
factory.proto = function() {
if(arguments.length === 0) {
return proto;
}
proto = slice.call(arguments).reduce(mixIn, proto);
return factory;
};
factory.type = function(factoryType){
type = factoryType;
return factory;
}
return factory;
}
StarShine.mixIn = mixIn;
if(module) {
module.exports = StarShine;
} else if(define) {
define(StarShine);
} else {
window.StarShine = StarShine;
}
}(typeof window !== 'undefined'?window:{},typeof define==='function'?define:null,typeof module!=='undefined'?module:null,[], Object);
view raw StarShine.js hosted with ❤ by GitHub

It's easy to overlook a small library like this. Here are a few of it's features.

  • StarShine() returns a factory. It's composed of the closures and objects that it gets passed.
  • It has built in internal storage and private "shared" variables. (More on that later)
  • Objects are defined by Javascript primitives:
    1. reusable closures
    2. properties
    3. methods
    4. prototypes
  • Works like a function composer
Each function closure should encapsulate a piece of functionality like this:
function defineA(opt) {
opt.a = "a";
//this gets mixed into the object
return {
"getA": function getA() {
return opt.a;
},
"setA": function setA(a) {
opt.a = a;
return this;
}
}
}
function exposedA(opt) {
//opt.a is defined here in this context too.
//You just inherited private shared variables from another context
return {
"doubleA": function(){
opt.a += opt.a;
return this;
}
};
}
function superPrivateVaraible() {
//functions are now like private storage you can manipulate
var a = "n";
//this is the object...
this.privateA = function(){
return a;
}
}
//Chainable API...
var AFactory = StarShine(defineA, exposedA, superPrivateVariable).type("AFactory");
AFactory();
//has doubleA, getA, setA, _type: "AFactory", privateA
I cannot stress more that being able to share private variables between closures is a crazy fundamental tool that every developer should use. I have personally used something like this in my personal code:
function _typeA(opt) {
opt.x = 0;
opt.y = 0;
opt.xV = 1;
opt.yV = 1;
}
function _typeB(opt) {
opt.x = 50;
opt.y = 50;
opt.xV = 1;
opt.yV = 0;
}
function _setType(opt) {
//direct property writing
this.x = opt.x;
this.y = opt.y;
this.xV = opt.xV;
this.yV = opt.yV;
//or
return {
x: opt.x,
y: opt.y,
xV: opt.xV,
yV: opt.yV
};
}
var A = StarShine(_typeA, _setType);
var B = StarShine(_typeB, _setType);
//Re-usable factory pieces are cool
Each factory has it's own prototype, take the following example.
function _helloWorld () {
return {
'hello': 'world'
};
}
var factory = StarShine(_helloWorld);
//the proto is already set to an object, so this only mixes methods into the prototype
factory.proto({
'getHello': function(){
return this.hello;
}
});
//this does NOT set the prototype reference
//no arguments returns the prototype
factory.proto();
//{
// 'getHello': function(){
// return this.hello;
// }
//}
view raw example1.js hosted with ❤ by GitHub
There is no way to make a prototype chain using this method, or maybe there is and I'm lazy, but this seems to be the only way I know to get prototypical inheritance:
factory1.proto(factory2.proto());
view raw proto.js hosted with ❤ by GitHub
If someone comes up with a better idea, I would love to modify my code to make it more flexible. Getting access to the prototype itself is useful with instanceof, but I highly doubt it is necessary. Chances are you probably just wanted to create a new prototype and set the _type property. Given the following example:
factory.type("CustomTypeHere");
factory();
//{
// "_type": "CustomTypeHere"
//}
view raw factoryType.js hosted with ❤ by GitHub

Accessing a simple property is probably going to be easier on your code and is simply faster.

This library is everything I love about javascript combined into one tiny little package, and I hope you like it.

No comments:

Post a Comment