‘This’ in JavaScript
zellwk.com
zellwk.com
We don't like it and it's redundant. One should learn the principles and return to this article to read it as a quiz and find out whether he got it or not.
Books like the excellent Javascript Allonge (free to read on leanpub) do a great job on explaining the context. If you need more hardcore stuff, "You don't know JS" will fit you better.
(Disclaimer: I'm not affiliated with any of the recommended books or their authors or leanpub or whatever.They just helped me a lot; and still do from time to time)
PSA Don't do this:
proto.foo = function() {
/* ... */
var self = this;
element.addEventListener("click", (function(event) {
self.iHaveAnUnhealthyObsessionWithLambdas(event.target);
}));
}
proto.iHaveAnUnhealthyObsessionWithLamdas = function(clicked) {
/* do something with clicked element `clicked` */
}
Do this instead: proto.foo = function() {
/* ... */
element.addEventListener("click", this);
}
proto.handleEvent = function(event) {
/* do something with clicked element `event.target` */
} class ElementDragger {
constructor(elm) {
elm.addEventListener('mousedown', this);
}
handleEvent(e) {
const elm = e.target;
switch(e.type) {
case 'mousedown':
elm.removeEventListener('mousedown', this).
elm.addEventListener('mousemove', this);
elm.addEventListener('mouseup', this);
break;
case 'mousemove':
elm.style.left = e.clientX + 'px';
break;
case 'mouseup':
elm.removeEventListener('mousedown', this);
elm.removeEventListener('mouseup', this);
elm.addEventListener('mousedown', this);
break;
}
}
}I don't know the purpose unbinding mousedown on mousedown - it only fires once per mousedown doesn't it?
Why not just stick with `elm.addEventListener('mousedown', this.handleMouseDown.bind(this))`? Where's the benefit other than making things more verbose?
> Where's the benefit other than making things more verbose?
To be clear what you're asking, are you saying that:
elm.addEventListener('mousedown', this);
... is more verbose than: elm.addEventListener('mousedown', this.handleMouseDown.bind(this))Switch statements in OOP are a sign that something is wrong.
handleEvent(e) {
this[e.type](e, e.target);
}
mousemove(e, elm) {
elm.style.left = e.clientX + 'px';
}
... but then the discussion would almost certainly revolve around this questionable decision instead of the original topic.Then again, my jugment might be colored by working on codebases that are >21 years old.
You're vacillating and applying an inconsistent standard. The switch statement is used where multiple listeners are being attached, therefore it wouldn't work with your "single function". You'd need multiple functions.
Give any example passing functions directly as the callback to `addEventListener` to demonstrate your case, please. The equivalent version that simply implements DOMEventListener will be less verbose, be lighter on resources (both with memory use and with runtime), and have no need for any this-binding workaround weirdness.
this._callback = this.handleMouseDown.bind(this);
elm.addEventListener('mousedown', this._callback);
elm.removeEventListener('mousedown', this._callback);
this._callback = null;
Compared to: elm.addEventListener('mousedown', this);
elm.removeEventListener('mousedown', this);
@richthegeek I removed that event listener in my example to illustrate how easy it is, there was otherwise no good reason for it.In general, a good practice is to avoid anonymous functions for the listener else it is not possible to remove the event later.
['click', 'focus', 'blur'].forEach(type => elm.removeEventListener(type, this));
The code using "self" doesn't have this problem; it's more self-contained so to speak (no pun intended).
One time I don't is in Jasmine, where they explicitly state that 'this' is shared across all 'beforeEach', 'it' and 'afterEach' calls. Which backs up your first point.
No, it's not. At all. It's part of the DOM Level 2 standard that specifies what `addEventListener` does and has been for going on 20 years.
https://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-...
You just moved the goalposts.
Exactly.
"If you can't explain it simply, you don't understand it well enough."
Albert Einstein
https://www.youtube.com/watch?v=PSGEjv3Tqo0
I've also started to write my code mostly without `this`, `class` or `new`, not looking back.
Another related, highly recommend reading: https://medium.com/javascript-scene/the-two-pillars-of-javas...
It doesn't bring anything (except a bit more performances when you create tons of objects, a setup that rarely matters unless we're talking video games or some very niche applications that somehow can't work with a subset of your data) but only create more cognitive load (bind, call, =>, closures using this, where Am I? is it a method or a function? Is it already pre-bound?).
Functionalities that create extra cognitive load and doesn't give you anything shouldn't be used.
As a related side rant, the `new` keyword, or equivalent, makes great sense in a manual-memory-management language like C, and it's an offense to sanity in a language like Java or Javascript. Factory functions should be the default everywhere. So any JS coding style that encourages `new` is a wildly bad fit to the rest of the language.
But it is important to admit the caveat, as you do, which is "when you create tons of objects".
Such situations are unequivocally in the minority (compared to line-of-business stuff where RAM and CPU more than sufficient to the task), but not the vanishing minority. So we do still need a good solution for those.
Rendering, simulation, sampling/signal-processing, parsing... the list goes on. This isn't truly systems-level code, but it's easy to generate a billion objects per second. So we do need patterns for doing this kind of programming.
Factory functions still need to create the object somehow to return it.
(If we're talking about Java, then obviously you have to have `new` somewhere, which is just one of the many shortcomings of Java, although obviously not a fatal one.)
Sorry, I don't follow your recommendation. Urgh, what a mess of a post.
All the (very valid) arguments that could be made for prototypical inheritance and functional programming style are drowned in a self-congratulatory fluff piece, full of marketing speech, substituting arguments by strawmen and namedropping, full of absolute claims and superlatives while demonstrating an rather pinhole view of computer languages, all the while not even giving a single practical example.
https://www.reddit.com/r/javascript/comments/6q2lk0/why_comp...
https://www.reddit.com/r/javascript/comments/5c5lkq/what_eri...
https://www.reddit.com/r/javascript/comments/3x91ac/why_not_...
I thoroughly understand `this` now after over a decade working in javascript, as well as thoroughly understanding `this` in Typescript which I use more now.
And I still make a mistake at least once a week. I know what I've done as soon as the code fails, but it's still so ridiculously easy to get into a context you didn't think you were.
Of all the mistakes made in javascript, `this` stands head and shoulders above everything else, with its arm round the prototypal class structure.
If there's anything that a decade of functional style being "the next big thing" has taught us, functional style is not "the next big thing" and most programmers don't work well in it.
it has dramatically reduced the amount of time i spend on these and many other sorts of problems. honestly it makes programming way more fun. maybe haskell is a bit extreme for most situations but the concepts are not.
What evidence do you have that functional is taking off? Since this board was around, every couple of years there's been a flurry of 3 months when a chunk of the community bangs on about functional programming.
First it was Lisp, then Erlang, then Haskell, then F#, then Scheme. I might have got the order wrong.
And it never, ever, ever takes off because while 10% of programmers find functional truly brilliant, the other 90% can't work with it.
For context, in that time Ruby (via Rails), Python (via Django and use in Science), javascript server-side (Node.js), Go, Swift, Rust and now possibly Kotlin have all taken off. Plus various other techs like NoSQL.
In the front end space, functional programming is taking off, I think. Could be wrong, but React + Redux + immutablejs is a popular and very functional approach to UI.
Elixir appears to be on the same trajectory as Rails in it's early days. Anectodally, I know a lot of Rails devs and shops that are now primarily Elixir.
Most mainstream languages have adopted functional styles and best practices. Higher order functions, a general acknowledgement of the benefits of immutability, etc.
Fp may not be mainstream, whatever that means, but it's more popular than I can ever remember it being.
Argue its advantages all you want, but I'd say that's representative of what a failure it's been. One of the last languages supporting prototype-based inheritance has basically given up on it.
Personally, I find it an awkward way to try and encapsulate code, generally, it has terrible performance to boot.
Actually, all this time, both Python and Ruby use what we would call prototype-based inheritance.
https://www.reddit.com/r/node/comments/6oqiyn/is_es6_complet...
https://www.reddit.com/r/learnjavascript/comments/6c6use/how...
The OR operator (||) is nice syntactic sugar for returning the first non-null value. Just like SQL's COALESCE.
let a = null
let b = 123
let result = a || b
result now equals 123.Dang it. Now I can't remember the other thing I like about JavaScript.
Oh, I do like using the shebang preamble for running nodejs scripts from the bash command line.
let a = 0
let b = 123
let result = a || b
sets result to 123, but 0 !== null.This may be confusing to people who expect `this` to have a certain functionality when coming from a different language, but `this` is very consistent in JavaScript. It's just not the kind of consistency people seem to expect.
There's a great way to avoid having the problem - don't use the implicit parameter! What exactly is it giving you, anyway? In almost all cases where people fail to use `this`, they essentially create a function to pass it explicitly. Why not just do that from the get go?
If you're doing a direct function call like myFunc() there is nothing "left" of the function call. But you might think of it as actually doing window.myFunc(), which again explains why this === window. In strict mode this behavior has been "fixed" so that this === undefined.
It's all about how a function is called.
(Spoilers: this == window, not what is left of the function call)
@getify explains it flawlessly, there are 4 modes: new, obj., bind/apply/call and default (window or error in strict mode)
Looking at a possible implementation of setTimeout makes it clearer:
function setTimeout(func, delay) {
// wait for the delay to complete..
func();
}
Here you can see that when the function is called there's nothing to the left of it. function Car() {
var car = this;
}
And if it already have a name, use that name: var foo = {
bar: 1,
say: function say() {
console.log(foo.bar); // foo instead of "this"
}
}; const sayThis = _ => console.log(this)
const boundFunc = sayThis.bind({hippy: 'hipster'})
boundFunc()
Because you used an arrow function, `this` is not {hippy: 'hipster'}. You can’t rebind functions; once they’re bound, they’re bound.There is surely a world of unexplained knowledge in that line for someone that doesn't know JS well
This removes the need for the `var self = this' hack and all the related weirdness.
An amusing consequence is that if you declare a simple closure as a method, its `this' pointer will be the stackframe of the lexical context where it is called.
method test() { print(this.a); }
var a = 5;
test(); /* implicitly __stackframe__.test() */
(You should not actually do this, of course. It's fun though.)The reason is straightforward: the `this' pointer is the object on whose lookup the closure was found...
class Test {
eval() {
return eval('this');
}
aliasEval() {
const e = eval;
return e('this');
}
}
const test = new Test();
assert.equal(test.eval(), test);
assert.equal(test.aliasEval(), global);(I can't believe it was 6 years ago, time flies.)
This made me avoid JavaScript as much as possible. Fortunately, I finally dived in, building my first js heavy webapp -- without a single this in it.
There are other ways to do it, but this struck me as the first time I had fiddled with "this" and felt that it was a reasonable idea.
I wish classes didn't make into ES6, object-oriented programming is a waste of time.
All the examples in that section both look correct to me, and run as I'd expect in the JS environment I have closest-to-hand.
If you want help on the syntax, you should pose a more-specific question. Exactly what code looks like an error, and why do you think it's an error, and so on?
Rather than:
{foo: foo, bar: function(){}}
You can just do: {foo, bar(){}} this = "madness!";
if (this === "madness!") {
this = "SPARTA!!!";
}
(sorry)Best way I can think to describe `this`:
-----
1. Either you decide in advance exactly what you want `this` to be:
- Use `.bind()` on your function and fix the value of `this`
- Create an arrow function and get `this` from the lexical scope
- Alias `let self = this` in your desired scope, and only ever reference `self`
- Create a wrapper function which fixes the value of `this` (essentially, implement `.bind()`)
-----
2. Or `this` will be decided by your caller:
- Your caller will `.bind()` your function with an unknown value of `this`
- Your caller will call `.call()` or `.apply()` with an unknown value of `this`
- Your caller will attach your function to an unknown object, then call `unknownObject.yourFunction()`, setting `this` to `unknownObject`
- Your caller will call your function as a standalone function: `let newFunc = someObj.someFunc` - then call it, setting `this` to `global` or `window`
- Your caller will pass your function as a param to something like `setTimeout` and achieve the same effect, setting `this` to `global` or `window`
-----
Obviously, most of the time if you're going to use `this`, #1 is to be preferred, #2 is to be avoided, so it's imperative to decide in advance what you want `this` to be rather than allowing it to be decided by your caller.
(Unless that's absolutely what you want, and you have a good reason why -- like if you're writing a wrapper function which is agnostic to the value of `this` that's passed)
I think it's an open question whether it was actually a horrifyingly bad design decision, or if it's rather that we're all too dumb to get it.
The real solution to the problem you're describing is strict mode and an editor that highlights undeclared variables!