How To Choose OO Design Patterns For JavaScript
Solution 1:
IFFE
s are often confusing to read and personally, I have no idea why they have become so mainstream. I think code should be easy to read and concise. Attempting to simulate language behavior that is not part of the language specification is often-times a very dumb idea.
For example, JavaScript does not support multiple inheritance, polymorphism, or many other interesting paradigms. So a lot of times, we see people trying to create these crazy ways of sorta'-kinda' having polymorphism or private members, etc in JS. I think this is a mistake.
I'm currently working as a sort of hobby project on a high-performance JS data structures library (I'm trying to outperform Google's closure and a bunch of others). Coming from a C++ and Java background, I always like to make stuff classes and I like inheritance, etc, etc. Let me share some code-snippets with you. At first, I thought I was being clever because I was writing stuff like this:
function __namespace(n, v) {
return {"meta":{"namespace":n,"version":v}};
}
var FJSL = FJSL == undefined ? new __namespace("Fast JavaScript Library", 0.1) : FJSL;
__using = function(parent, child) {
clazz = new child();
clazz.super = new parent();
if (clazz.super == undefined) return clazz;
for (a in clazz.super) {
for (b in clazz) {
if (a == "constructor" || b == "constructor") continue;
if (clazz[b] === clazz.super[a]) continue;
if (a == b && typeof clazz[b] != typeof clazz.super[a]) throw "Typesafety breached on '" + a + "' while trying to resolve polymorphic properties.";
if (a == b && typeof clazz[b] == typeof clazz.super[a]) {
clazz["_"+a] = clazz.super[a];
} else if (clazz[a] == undefined) {
clazz[a] = clazz.super[a];
}
}
}
return clazz;
};
And I was using it like so (in the example of a simple Queue):
FJSL.Array = function() {
this.data = [];
this.contains = function(idx, element) {
for (var i = idx; i < this.data.length; i++) {
if (this.data[i] === element)
return i;
}
return -1;
}
this.size = function() {
return this.data.length;
}
}
FJSL.Queue = function() {
return __using(FJSL.Array,
function() {
this.head = 0;
this.tail = 0;
this.enqueue = function(element) {
this.data[this.tail++] = element;
};
this.dequeue = function() {
if (this.tail == this.head)
return undefined;
return this.data[this.head++];
};
this.peek = function() {
return this.data[this.head];
};
this.size = function() {
return this.tail - this.head;
};
this.contains = function(element) {
return this._contains(this.head, element);
};
}
)};
You'll note how I'm sort of faking inheritance (a Queue uses an Array, har har, I'm clever). However, this is absolutely insane to a) read and b) understand. I couldn't help but be reminded of this meme:
Let me show you functionally equivalent code without me trying to do all this fancy pre- and post-processing:
FJSL.Queue = function(opts) {
this.options = opts;
this.head = 0;
this.tail = 0;
this.data = [];
};
FJSL.Queue.prototype = {
add : function(element) {
this.data[this.tail++] = element;
},
enqueue : function(element) {
this.data[this.tail++] = element;
},
dequeue : function() {
if (this.tail == this.head) {
return undefined;
}
return this.data[this.head++];
},
peek : function() {
return this.data[this.head];
},
size : function() {
return this.tail - this.head;
},
contains : function(element) {
// XXX: for some reason a for : loop doesn't get JIT'ed in Chrome
for (var i = this.head; i < this.data.length; i++) {
if (this.data[i] === element) {
return true;
}
}
return false;
},
isEmpty : function() {
if (size) {
return true;
}
return false
},
clear : function() {
this.data = [];
}
};
Obviously, I'd have to duplicate the prototype constructor for any other structures that may use an array, but what I'm trying to accomplish is so much clearer, even a novice JS programmer can tell what's going on. Not only that, but if people want to modify the code, they know exactly where to go and what to do.
My suggestion is don't get caught up in the insanity of trying to make JS behave like C++ or Java. It's never going to. And yeah, you can fake inheritance and private/public/protected members, but JS was never intended for that. I think the repercussion of having this kind of bloated code (that attempts to simulate non-standard behavior) is very taxing on high-performance web-apps and their ilk.
In short, I suggest using an object literal:
var public_statics = {
public_func: function () {},
public_var: "hello"
}
It's easy to understand, easy to modify, and easy to extend. If your system is brittle enough to crash and burn if someone accidentally changes some "private" variable, you simply need to document it.
Solution 2:
I personally prefer the IIFE simply because you can make methods private. Otherwise, you will have to do some sort of weird convention with underscores.
Furthermore, stylitically, if you encapsulate it within a function, you have the option of making semicolons - its plain old javascript. Requiring each line in the object literal to end with a comma seems funny to me.
Post a Comment for "How To Choose OO Design Patterns For JavaScript"