Skip to content

Instantly share code, notes, and snippets.

@ohager
Created June 14, 2016 17:00
Show Gist options
  • Save ohager/9908284598c5c04922d7e8b331c1b295 to your computer and use it in GitHub Desktop.
Save ohager/9908284598c5c04922d7e8b331c1b295 to your computer and use it in GitHub Desktop.
Dextra BrownBag: Better JS
// 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
@anderson-otuka-dextra
Copy link

Nice repertoire 😮

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment