Javascript: is 'this' a code smell?
markbernstein.org
markbernstein.org
It is not as if you have to look far javascript awkwardness - you just have to live with some small but ugly workaround. My guess in this case aliasing "self" to "this" at the top level of deeply nested objects would probably solve the problem.
The article author will also be a lot happier when he realizes that Javascript might look like Java but it really wants to be a lisp.
Sometimes I wonder if people just mean, "Wow, Javascript is actually a really expressive, dynamic language, once you get past all the idiosyncratic DOM stuff", but don't know enough other non-ALGOL languages to see that expressiveness isn't just a Lisp thing. Because hey, it doesn't have s-expressions, macros/static metaprogramming, an explicit compile-time phase at all, etc. It has much more in common with Lua or Self than Lisp. It could have been a really awesome language if the browser wars hadn't stunted its growth.
The reason I said lisp is because Brendan more or less always mentions he wanted to implement a scheme interpreter but Netscape marketing insisted that it look like Java.
As for more closely resembling Self/Lua, I'd largely agree if for nothing other than the obvious object system similarities. On the other hand, I think the idiosyncrasies of javascript like `this` and call/apply, the enforced n-arity of functions, and a lot of the 'convert it to a string' fallbacks (think lisp 1) were attempts at fitting lisp concepts onto the self-like object system. Of course, I have no basis for this so I'm probably wrong, but it makes me feel better when I have to explain them.
I will note that JS is moving into limited metaprogramming with the harmony proxies proposal. I also consider destructuring (another harmony proposal) to be a more functional concept than self/lua, but I actually don't know where that started.
I'm also positing Crockford-style modules.(Since it's pretty clear from the context that I'm following Crockford, you might assume that I know about the Javascript/LISP connection.)
The question remains: is repeated use of "this"
this.Mill();
this.Drill();
this.Fill();
simply semantic vinegar, or does it suggest that the object is not doing its job -- or that we need a new object?Having come from Python to Javascript, I consider this to be explicit and a feature rather than a code smell. I dislike when a language's include constructs dump everything into the local namespace and the `this` is implicit because it means that I can't look at a piece of code in isolation without IDE support and figure out where a particular reference is resolving.
If it really bothers you, coffeescript uses @ in place of this:
Product::make = () ->
@mill()
@drill()
@fill()One of the main features that CoffeeScript adds to help with "this" issue is the bound function literal (the fat arrow). A regular function looks like this:
add = (num) -> this.value += num
And compiles into the regular JavaScript you'd expect: var add;
add = function(num) {
return this.value += num;
};
However, if you define the function with a fat arrow instead: add = (num) => this.value += num
You'll get a function bound to the current value of "this", and when you pass the function off to another object as a callback -- a DOM event handler, or success callback from an Ajax request -- the value of "this" doesn't change.Here's perhaps a better example, from jQuery:
var self = this;
$(".row").bind("click", function() {
this.setText("Clicked!");
});
And in CoffeeScript, without the "self = this" bit: $(".row").bind "click", =>
this.setText "Clicked!"I tend to think of them as "bag-of-things" languages, since they also all share the fact that objects are just tables/dictionaries, and the only thing that distinguishes an attribute reference from a method call is the "()" at the end. Certainly there's a more accepted nomenclature?
I don't know if there is, but there should be.
The defining aspect seems to be that there aren't pure ADTs, just collections based an associative arrays ("dicts" / "tables"), and methods are just table fields that contain a function. Also, Python has classes where Lua really does just have pure tables with hooks ("metatables", which make it easy to implement prototypes, classes, and many other constructs).
While there isn't really a term (that I'm aware of) specifically for the this/self convention, both are idiomatic examples of dynamic scoping (compared to lexical scoping); that is, scope is determined at runtime by the most recent stack frame to define a given variable (ie, foo defines "this", then calls "bar", bar sees "this" as it was defined by foo), rather than at lex time.
Person = function() {
this.say = function(phrase) {
console.log(phrase);
}; Person.prototype.say = this.say;
};
Developer = function() {
var mypriv = "I have encapsulation";
this.say = function(phrase) {
Person.prototype.say.call(this, "Hello");
console.log("I develop "+phrase);
console.log(mypriv);
};
}; Developer.prototype = new Person();
var devel = new Developer();
devel.say("a lot");
Coming from a C++ and Java background, I have put this pattern together from various sources: Crockford, John Resig and others. It has worked great and I dont see any problems with structuring your large JS program in this way.if i really need OO, i use YUI and forget about most javascript caveats. Everything is just too 'wrong' to keep track in your mind.
Every time you create a new instance of a Developer, you redefine every single function. This makes instantiation far slower than it would be if you used prototypes, and leaks memory like crazy: N Developers means N copies of each function.
So, first, it's slow -- take this test: https://gist.github.com/459112
Webkit: http://tinyurl.com/2csgzez Firefox: http://tinyurl.com/29wd2ms
And second, the memory use. Take a moderately-sized class, with ten member functions. The last time I benchmarked it in Chrome, using real prototypes, it takes a little under 32 megabytes of RAM to create a million instances of it. Try to create a million instances using the pattern you have above, and it spins the browser for minutes, and eats up 368 megabytes of RAM. IE just crashes.
If you're doing OOP in JS -- please use prototypes as Eich intended...
Im aware of that the function is defined in each object, and that it takes up memory. I dont agree that the memory is leaked, it is reclaimed when the object is deleted.
As I never create millions of instances of objects, and this patterns dont seem to have a measurable impact on the rendering loop in my project, Ill just continue using it for now.
I would call the pattern you propose to be the "object factory" pattern. And I am curious how to do classical inheritance and encapsulated vars with it.
Boom. Get it while it's hot.
Lua has syntactic sugar where table:method(arg1, arg2, ...) is exactly equivalent to table.method(table, arg1, arg2, ...). All it really does is establish a convention and eliminate "self" from argument listings in methods, but it's still handy - its presence is an immediate sign of OO-style code.
For me, it helps the way my mind thinks, I instantly know hey, this thing is not local, be careful.
The bigger issue to me is that functions can be passed independent of class in JavaScript, so you have to have some way of making sure the object tags along for the ride.
There are various implementations to do this such as Dojo's dojo.hitch(this, "foo"); which, is not that bad given that you can hand stuff off for callbacks, it is a pretty good trade off. It just takes some getting used to, but the JavaScript is a poorly designed language is a tired meme. I have used it from the day it was released and never saw what all the fuss was about. There are some design decision I would not have made, like no matter where you scope a variable in a routine it is visible to the entirety of that routine. But none of it is so offensive as to earn it the reputation it has had. Further subsequent standards also went a long way to improve on many of the early shortcomings as well.