On Github dilvie / fluent-prototypal-oo
"Those who are unaware they are walking in darkness will never seek the light."
~ Bruce Lee
How to Think in Prototypal OO
Eric Elliott
... but no class imitation has ever become a defacto standard.
Why is that?
"The problem with object-oriented languages is they've got all this implicit environment that they carry around with them. You wanted a banana, but what you got was a gorilla holding the banana and the entire jungle."
~ Joe Armstrong, "Coders at Work"
For years I built apps in JavaScript, forgetting that these problems ever existed...
"Favor object composition over class inheritance."
~ The Gang of Four, "Design Patterns"
A working sample.
It's that simple.
function Greeter(name) { this.name = name || 'John Doe'; } Greeter.prototype.hello = function hello() { return 'Hello, my name is ' + this.name; } var george = new Greeter('George');
var proto = { hello: function hello() { return 'Hello, my name is ' + this.name; } }; var george = Object.create(proto); george.name = 'George';
var proto = { hello: function hello() { return 'Hello, my name is ' + this.name; } }; var george = _.extend({}, proto, {name: 'George'});
var foo = _.extend({ attrs: {}, set: function (name, value) { this.attrs[name] = value; this.trigger('change', { name: name, value: value }); }, get: function (name, value) { return this.attrs[name]; } }, Backbone.Events);
Can replace:
var model = function () { var attrs = {}; this.set = function (name, value) { attrs[name] = value; this.trigger('change', { name: name, value: value }); }; this.get = function (name, value) { return attrs[name]; }; _.extend(this, Backbone.Events); };
var george = {}; model.call(george).set('name', 'George'); george.get('name'); // 'George' george.on('change', function (event) { console.log(event.name, event.value); }); george.set('name', 'Simon'); // name Simon
Lots of hoop-jumping to use all these features together.
Create objects from reusable, composable behaviors.
Getting started (Node):
$ npm install stampit
Getting started (without Node):
$ git clone git://github.com/dilvie/stampit.git
var a = stampit().enclose(function () { // Secrets go here: var a = 'a'; // Methods can access secrets. // Nothing else can. this.getA = function () { return a; }; });
a(); // Object -- so far so good. a().getA(); // "a"
var b = stampit().enclose(function () { // This var won't collide with the other `a` var a = 'b'; this.getB = function () { return a; }; });
var c = stampit.compose(a, b); var foo = c(); // we won't throw this one away... foo.getA(); // "a" foo.getB(); // "b"
Can your class library do that?
ES6 class can't.
var availability = stampit().enclose(function () { var isOpen = false; // private return stampit.extend(this, { open: function open() { isOpen = true; return this; }, close: function close() { isOpen = false; return this; }, isOpen: function isOpenMethod() { return isOpen; } }); });
var membership = stampit({ add: function (member) { this.members[member.name] = member; return this; }, getMember: function (name) { return this.members[name]; } }, { members: {} });
var defaults = stampit().state({ name: 'The Saloon', specials: 'Whisky, Gin, Tequila' });
"Favor object composition over class inheritance."
var bar = stampit.compose(defaults, availability, membership); // Note that you can override state on instantiation: var myBar = bar({name: 'Moe\'s'}); // Silly, but proves that everything is as it should be. myBar.add({name: 'Homer' }).open().getMember('Homer');
“When there is freedom from mechanical conditioning, there is simplicity. The classical man is just a bundle of routine, ideas and tradition. If you follow the classical pattern, you are understanding the routine, the tradition, the shadow – you are not understanding yourself.”
~ Bruce Lee