6 ms·
Javascript: is 'this' a code smell?
- cosbynator 16y agoThis article has very little content beyond a vague complaint about the keyword 'this' without actually showing of examples of the problem. 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.
- CoffeeDregs 16y agoI second ... er ... this. I read the article and was pretty much 100% confused.
- makuro 16y agohttp://www.amazon.com/JavaScript-Patterns-Stoyan-Stefanov/dp/0596806752 http://www.amazon.com/JavaScript-Patterns-Stoyan-Stefanov/dp... Boom. Get it while it's hot.
- axiak 16y agoI don't understand what the issue with explicitness is. Python uses self and people seem to be pretty OK with that.
- silentbicycle 16y agoIt's really not a big deal. Just a bit cumbersome. 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.
- kls 16y agoRight, I in fact like using the "this" reference, I use it in Java and C# as well when accessing any member variable. 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.
- ghotli 16y agoI'd say this guy is not adapting to the expressiveness of the language. He wants objects, but he's using a prototypical closure oriented language. 'this' is a needed construct to deal with the malleable scope contexts that closures provide.
- dmatking 16y agoIt does make me question why the books and tutorials I have read on JavaScript want to cover so many object-oriented techniques. Why not treat it more like a functional or "prototypical closure oriented" language? Perhaps the author is on to something.
- blasdel 16y agoBecause it's so much more ironic to brutalize the language with classicist inheritance and call it 'Prototype.js'
- grayrest 16y ago`this` is not a code smell, but trying to do variable hiding/encapsulation of attributes through closures leads to grotty code. Use the leading underscore convention. It's worked in Python for years. 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.
- silentbicycle 16y agoTo me, Javascript seems headed in the direction of Self, not Lisp. Not a bad place to end up, by any means, but why do people think Javascript is trying to be Lisp in particular? (I know Eich was influenced by Lisp, but so were ML and Smalltalk.) 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.
- grayrest 16y ago> why do people think Javascript is trying to be Lisp in particular? 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.
- eastgate 16y ago
- Detrus 16y agoThe syntax is definitely awkward. But efforts like coffee-script http://jashkenas.github.com/coffee-script/ http://jashkenas.github.com/coffee-script/ are here to alleviate some of these pain points.
- amadiver 16y agoUnfortunately, CoffeeScript doesn't support implicit "this". I'm working on that problem, and I hope to share it soon.
- jashkenas 16y agoThe main problem with JS "this" is that in a language where everything else is lexically scoped, "this" is dynamically scoped -- taking the value of whatever object the function happens to be attached to at call time. To use a Ruby metaphor, imagine if "instance_eval" were the default way to call a method. 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!"
- shubber 16y agoBoth Python and Lua share this characteristic with Javascript. Is there a name for this language feature, or for languages that share it? 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?
- silentbicycle 16y ago> 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).
- cheald 16y agoWhat you're terming "bag-of-things" languages can probably be more aptly described as languages which have both dict/tables/objects and first-class functions. 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.
- shubber 16y agoWhile there's a correlation between dynamic scoping and explicit "this", it isn't universal: Ruby being the obvious exception.
- silentbicycle 16y agoDijkstra would not have liked 'this'.
- bni 16y agoRecently I have been having a good time developing a large program with this classical inheritance pattern. 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.
- gcb 16y agoi simply avoid writing "new" in javascript. ever. well, unless its for Date. 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.
- bni 16y agoEverything felt wrong when I started programming in JS for real about a year ago, but now I would say I understand most aspects of it, and can keep track. Im still not ready to let go of classes and classical inheritance though. Not sure if I ever want to.
- ichverstehe 16y agoTake a look at Io. It does prototypes properly. And they are, in fact, pretty nice to work with. I wouldn't mind getting rid of classes in my day-to-day life.
- jashkenas 16y agoPlease, please don't spread this "object factory" pattern any further than it's already gone. It's an incredibly inefficient way to do OOP in JavaScript. 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 https://gist.github.com/459112 Webkit: http://tinyurl.com/2csgzez http://tinyurl.com/2csgzez Firefox: http://tinyurl.com/29wd2ms 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...