Created
June 14, 2016 17:00
-
-
Save ohager/9908284598c5c04922d7e8b331c1b295 to your computer and use it in GitHub Desktop.
Dextra BrownBag: Better JS
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// our swiss army knife for javascript | |
var _ = require('lodash'); | |
// ------------ Helper | |
// recursive freezing | |
function deepFreeze(obj) { | |
// Retrieve the property names defined on obj | |
var propNames = Object.getOwnPropertyNames(obj); | |
// Freeze properties before freezing self | |
propNames.forEach(function(name) { | |
var prop = obj[name]; | |
// Freeze prop if it is an object | |
if (typeof prop == 'object' && prop !== null) | |
deepFreeze(prop); | |
}); | |
// Freeze self (no-op if already frozen) | |
return Object.freeze(obj); | |
} | |
// -------------------- | |
// namespace pattern | |
var GodSpace = GodSpace || {}; | |
// base "class" | |
function Item(name, description, weight) { | |
this.weight = weight; | |
this.name = name; | |
this.description = description; | |
} | |
Item.prototype.use = function(){ | |
console.log("Humm, I dunno how to use this item"); | |
}; | |
// prototype inheritance pattern | |
function Katana(){ | |
// super call | |
Item.call(this, "Katana","Very sharp edge", 2.5 ); | |
} | |
Katana.prototype = Object.create(Item.prototype); | |
Katana.prototype.constructor = Katana; | |
// override | |
Katana.prototype.use = function(){ | |
console.log("*swish* *swush* - chopped in half"); | |
}; | |
// prototype inheritance pattern | |
function RedPill(){ | |
// super call | |
Item.call(this, "Red Pill","Reality Distorter", 0.01 ); | |
} | |
RedPill.prototype = Object.create(Item.prototype); | |
RedPill.prototype.constructor = RedPill; | |
RedPill.prototype.use = function(){ | |
console.log("*gulp* . . . Wow?!"); | |
}; | |
// ------------ Inventory 'class' ----------------- | |
function Inventory(){ | |
this._items = []; | |
} | |
// 'shared' member functions by using prototype | |
Inventory.prototype.addItem = function(item){ | |
deepFreeze(item); // make it immutable | |
this._items.push(item); | |
}; | |
Inventory.prototype.getWeight = function(){ | |
return this._items.reduce(function(previousResult, currentItem){ | |
return previousResult + currentItem.weight; | |
},0) | |
}; | |
Inventory.prototype.getItems= function(){ | |
return this._items; // considered immutable | |
}; | |
Inventory.prototype.getItemByName= function(name){ | |
// lodash find function, somewhat like mongodb queries | |
return _.find(this._items, {name : name}); | |
}; | |
// ------------ Our Hero class in our namespace | |
GodSpace.Hero = function(inventory){ | |
this._inventory = inventory; | |
function init(){ | |
var items = inventory.getItems(); | |
console.log("-=* HERO SUMMONED *=-"); | |
console.log("Our hero's inventory", JSON.stringify(items, null, " ")); | |
console.log("It weights ", inventory.getWeight(), " kg" ); | |
} | |
init(); | |
// a common way to expose functions of instance, | |
// but this way the function object will be created each | |
// time the hero is instantiated... better is to use GodSpace.Hero.prototype.getInventory | |
this.getInventory = function(){ | |
return this._inventory; | |
}; | |
}; | |
// | |
var BerzerkerFeature = function(){ | |
// we expect *this* to be a Hero instance | |
// we obtain it through object binding with bind(), apply(), or call() (see how the feature is used on composition) | |
if(!this instanceof GodSpace.Hero) throw "You can apply the Berzerker Feature only for Heroes"; | |
var _items = this.getInventory().getItems(); | |
function _roar(item) { | |
var repertoire = [ | |
"You bloody bastard...your time has come, eat my " + item.name, | |
"Hey, you sneaky leaky crap. I have this nifty lil thing for u, known as " + item.description, | |
"Hey, you dirty bastard villain, I have a " + item.name + ".\nAND I'M GONNA USE IT!", | |
"Hohoho, you slicky neat poopoo lover! Look that nifty item here. It's a " + item.name + ".\nAND I'M GONNA USE IT!", | |
"Ya dirty faggot, watcha diz " + item.description + ", which I'm gonna use it to kick the shit outta ya!", | |
"Hi, my friend. I have some fat balls on fire, and a " + item.name + "...watch out right now!" | |
]; | |
// just another neat example of lodash, randomized selection from array | |
console.log(_.sample(repertoire)); | |
item.use(); | |
} | |
// facade pattern | |
return { | |
berzerk: function () { | |
_items.forEach(_roar); | |
} | |
} | |
}; | |
function MetaTest(hero){ | |
// 'private' members | |
var _hero = hero; | |
function checkInstanceTypes (){ | |
var items = hero.getInventory().getItems(); | |
console.log("---- Checking instance types"); | |
items.forEach(function(item){ | |
var text = "Item '" + item.name + "' is "; | |
console.log(text + "Item", item instanceof Item); | |
console.log(text + "Katana", item instanceof Katana); | |
console.log(text + "RedPill", item instanceof RedPill); | |
}); | |
} | |
function proveImmutability() { | |
var items = hero.getInventory().getItems(); | |
console.log("---- Prove immutability -----"); | |
console.log("Name is: " + items[0].name); | |
console.log("Trying to change first items name..."); | |
items[0].name = "SomeCrazyName"; | |
console.log("Name is now: " + items[0].name); | |
console.log("Changing array:"); | |
items = []; | |
console.log("Array still has " + hero.getInventory().getItems().length + " items" ); | |
console.log("---------"); | |
} | |
// js facade pattern, leveraging lexical scope and closures | |
// this is another elegant way to expose functions/object | |
return { | |
go : function(){ | |
checkInstanceTypes(); | |
proveImmutability(); | |
} | |
} | |
} | |
// ------------ entry point, using anonymous self-calling function (with injection) | |
var inventory = new Inventory(); | |
// our hero is a double handed fighter! | |
inventory.addItem( new Katana() ); | |
inventory.addItem( new Katana() ); | |
inventory.addItem( new RedPill() ); | |
(function(inv){ | |
var hero = new GodSpace.Hero(inv); | |
// here we call BerzerkerFeature, but we *bind* our hero to the call, | |
// so that inside the BerzerkerFeature function *this* becomes our hero instance | |
// using *bind* it would look like this: | |
//var berzerkFeatureFunc = BerzerkerFeature.bind(hero); // no call | |
// var heroesBerzerkerFeatures = berzerkFeatureFunc(); | |
var heroesBerzerkerFeatures = BerzerkerFeature.apply(hero); | |
// inheritance by composition | |
// our hero is a berzerker! | |
Object.assign(hero, heroesBerzerkerFeatures); | |
MetaTest(hero).go(); | |
console.log("Our hero is *very* angry"); | |
console.log("BERZERK MODE\n--------------"); | |
hero.berzerk(); | |
})(inventory); // injection |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Nice repertoire 😮