I'm currently writing objects in javascript and I would like to do it in a clear, nice way, using best practices etc. But I'm bothered that I must always write this.
to address attributes, unlike in other OO languages.
So I got the idea - why not just use closures for object attributes? Look at my example object. So instead of this, classical way:
var MyObjConstructor = function (a, b) {
// constructor - initialization of object attributes
this.a = a;
this.b = b;
this.var1 = 0;
this.var2 = "hello";
this.var3 = [1, 2, 3];
// methods
this.method1 = function () {
return this.var3[this.var1] + this.var2;
// terrible - I must always write "this." ...
};
}
... I would do it using closure and then I don't need to write this.
every time to access the attributes!
var MyObjConstructor = function (a, b) {
// constructor - initialization of object attributes
// the attributes are in the closure!!!
var a = a;
var b = b;
var var1 = 0;
var var2 = "hello";
var var3 = [1, 2, 3];
// methods
this.method1 = function () {
return var3[var1] + var2;
// nice and short
};
// I can also have "get" and "set" methods:
this.getVar1 = function () { return var1; }
this.setVar1 = function (value) { var1 = value; }
}
Also, it has a hidden benefit that the attributes really cannot be accessed any other way than by get/set methods!!
Disadvantages of closures There are two main disadvantages of overusing closures: The variables declared inside a closure are not garbage collected. Too many closures can slow down your application. This is actually caused by duplication of code in the memory.
Closures are frequently used in JavaScript for object data privacy, in event handlers and callback functions, and in partial applications, currying, and other functional programming patterns.
Objects are a subset of closures, so they carry some similar traits. Note: objects and closures over lexical scopes are equivalent. Idiomatic style of programming in a language that allows nested function definitions takes advantage of this fact, rather than being confounded by it.
Function objects (C++) As of the 2011 revision, the C++ language also supports closures, which are a type of function object constructed automatically from a special language construct called lambda-expression.
95% performance decrease.
An actual Benchmark so for your (simple) example were talking 50%-85% performance decrease across browsers.
Seriously, closures are slow as hell.
Now using closures for data isn't the problem, but using closures for functions/methods is. And you can't do one without the other. Methods that live on the prototype have no mechnanism of accessing local variables that live inside the constructor.
And the other problem is your "classical" example doesn't use the prototype :\
What you really want is
So the following is also bad.
var MyObjConstructor = function (a, b) {
// constructor - initialization of object attributes
this.a = a;
this.b = b;
this.var1 = 0;
this.var2 = "hello";
this.var3 = [1, 2, 3];
// methods
this.method1 = function () {
return this.var3[this.var1] + this.var2;
};
}
You want
// constructor - initialization of object attributes
var MyObjConstructor = function (a, b) {
this.a = a;
this.b = b;
}
Object.extend(MyObjConstructor.prototype, {
var1: 0,
var2: "hello",
var3: [1, 2, 3],
// methods
method1: function () {
return this.var3[this.var1] + this.var2;
}
});
For some value of Object.extend. Here were placing any common data or methods on the prototype and sharing that data among all instances. This way we are not memcopying everything everytime everywhere.
// terrible - I must always write "this." ...
The alternative is duplicating state for every single object. The closure pattern is nice and all but it's just not performant.
To answer your questions directly:
1. Is it OK to use objects as closures? Is it conforming to best practices?
Yes. In some situations you really want to have private fields so this is the only way one the ways to do that. For a real, concrete, example have a look at Deferreds/Promises in Dojo or JQuery. The Promises implement just the non-mutating subset of deferreds so they need to keep their inner Deferred private to avoid others from changing it directly.
Do remember that you should only really use hidden fields where you really need them (and not for trivial reasons like not having to type "this"). Using plain old public fields and "normal" objects is also perfectly fine, especially if you consider that they have some advantages that the closure version doesn't have:
2. Is there any semantic difference between these two versions?
Yes.
3. Are there any traps on using a closure like this?
Most Javascript engines today are less performant on code with lots of closures compared to code that uses prototypes. Not a "real" reason for a difference (since LISP engines have been fine with closures since forever) but something you will have to live with.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With