I think the main reason "this" is considered confusing is that many programmers learning JS have already spent years coding in Java or C#. They expect it to work the same, then get frustrated when it doesn't. But the reason it doesn't is due to the fact that functions in JS are first-class, and don't intrinsically belong to a particular class and object instance they way they do in those languages. As such, the context represented by "this" has to be more fluid in JS.
That's not to say that the design of "this" is perfect. And it doesn't help that JS' syntax was designed to resemble C-family languages that it differs from significantly. But nor it is some entirely arbitrary and illogical flaw. If you take the time, it's possible to gain an intuitive grasp of how functions and "this" work in JS. But instead, learners are too often told that "JS is quirky" by others who also have come to it from Java / C#, and never move past memorizing to a list of these "quirks" to genuine proficiency. They then grumble whenever they need to work on a JS codebase, because their incomplete understanding makes it feel like they're standing on quicksand.
Soon such devs fall into a circular trap: They don't like coding in JS because it feels weird and unintuitive. And they don't take the time to learn about it more deeply because they don't like coding with it, and don't want to waste time learning about something they dislike. But since they have to code with it, because it's part of their job, they just stumble along, hacking away and getting frustrated when things don't work, then come onto Hacker News and blow off steam about what a garbage language JS is.
> due to the fact that functions in JS are first-class
I don't agree with that. There are plenty of languages with first-class functions that don't have this kind of confusion. There's nothing about a function being a value and being able to pass functions around that necessitates the awkwardness of JavaScript's 'this'.
What does necessitate it is the lack of separation between functions and objects. The fact that a function can also have its own properties is, to my mind, a mistake in the design of the language. And it's a decision that those other languages with first-class functions and no 'this' problem didn't make. If objects and functions are distinct concepts in the language, it's trivial to make 'this' be dead simple and allow programmers to have a simpler mental model.
You're correct that other languages have first-class functions which are capable of handling context without the implicit-this style like JS. Rust, for instance, uses `self`-as-first-parameter style.
However, `this` has nothing to do with functions being objects, besides the fact that the prototype object is a member to the constructor function.
In perl/c[++]/#/java, you have to carry object around with a method (or use functors), otherwise you’ll call it with wrong arguments. In python methods are bound implicitly in a class, but explicitly in assignment (iirc). In Lua you’re explicit with obj:mtd() instead of obj.fun(). In js, obj.mtd() is eqv. to obj:mtd() in that sense, but you can bind() to anything.
Which model is preferable? ‘this’ is natural complexity, and under ‘use strict’ it behaves just okay. It is programmers who don’t care to grasp it.
> In python methods are bound implicitly in a class, but explicitly in assignment (iirc).
Oh, it's quite a bit more involved than that :)
I'm going to ignore Python 2 and the C API here.
In Python functions are descriptors. This means they have (mostly) a __get__ function which returns a closure passing self or cls or nothing (corresponding to a regular method, a class method and a static method; @classmethod and @staticmethod change the implementation of __get__, but of course they don't, because everything is a special case in CPython; note that "regular methods" are "regular functions" — all functions behave like methods if you treat them like one). If you write
class Class:
def foo(self, x): print(x)
then foo is a regular old function. The magic is in the dot operator (quite literally): instance.foo is a different operator from Class.foo, and instance.foo is not Class.foo (one is a bound method the other is a plain function). The former invokes the descriptor protocol to the effect of, roughly, type(instance).__dict['foo']__.__get__(type(instance), instance), which returns a closure calling the "real foo" (Class.foo) with instance as the first argument. Similarly @classmethod has the effect of __get__ returning a different closure, which passes type(instance) as the first argument instead.
Having very different semantics hiding under identical syntax is still a design fault with the language though, especially considering that Javascript's adherence to Java syntax is deliberate not accidental.
I really don't blame programmers for getting confused by this.
>I think the main reason "this" is considered confusing is that many programmers learning JS have already spent years coding in Java or C#. They expect it to work the same, then get frustrated when it doesn't. But the reason it doesn't is due to the fact that functions in JS are first-class, and don't intrinsically belong to a particular class and object instance they way they do in those languages. As such, the context represented by "this" has to be more fluid in JS.
As someone who’s “native” language is JS, I think that’s spot on. I never had the baggage of thinking in terms of Java or C++, and so I couldn’t understand why people would get confused over “this” because it was fundamental to learning the language and thus to my mental model of programming in general.
I believe that’s because you ask ‘what this really is’ and get another ton of bullshit articles that never do a strict definition of the mechanism, instead resorting to vague examples with bad names. MDN included.
Java ushered in with it, a sort of tunnel vision that class-based inheritance was _the_ fundamental theorem of business programming. When people see prototypal inheritance like in JS, they call it "quirky" or "poorly designed", as if it were haphazardly evolved as opposed to deliberately designed. When languages like Go or Rust came out, it was shocking how many times I saw "where's classes?" come up on the mailing list.
'this' is confusing because it is a novel abstraction that has no analog/referent to anything familiar, even if JavaScript is your first language. It is completely unintuitive and simply must be internalized with time
Effectively, 'this' forces you to understand the majority of the prototypal nature of JavaScript just to simply call some functions.
Alternatively, don't use this at all, ever. You can pass explicit arguments for anything you need.
Sums it up pretty good. 'this' was a minor problem when promises came out and bind got sort of sprinkled everywhere. Arrow notations can now take care of that by making it less verbose by binding 'this' behind the scenes.
This would have been a good post in 2014 but it shouldn't be relevant to any JavaScript developers in 2018.
Arrow functions don't have `this`, and `...` syntax is simpler and as efficient as using `apply`. You only still need to use `function(){}` for generators and custom constructors, which are rare.
Very relevant in 2018. React uses ES6 classes (inherit from React.Component) so you're very often using `this` in React. In fact it's still a major source of confusion still for many devs like me, especially when you create and pass callback functions. I've been meaning to write a blog post on this so maybe this is the motivation I needed.
This results in extra renders for an entire component chain, because comparing two arrow functions will always be different whereas comparing two pre-defined functions will always be the same. Hence React's recommendation to use 'bind'.
No, that's only true _if_ a child component is explicitly attempting to optimize its performance by doing shallow equality checks on its props in `shouldComponentUpdate` (or is a `PureComponent`, which does the same thing automatically). Otherwise, React's default behavior is to re-render a component tree all the way down anyway, so it doesn't matter whether a given prop has actually changed its reference or not.
See this post by Ryan Florence that explains why "arrow functions in render" is not a big deal:
What I don't get is why the React guys don't just call the passed functions with 'this' being the component, which seems like the most common use case. If you need to override this, you could indeed use bind (it's not overridable after that AFAIK so no logic is required from React side).
Because it's really _not_ "the most common use case". Components often pass down callbacks through multiple levels of children. A basic example would be a Form component that renders something like:
<Button onClick={this.onFirstNameChanged} />
The proper context for `this` is the Form, but the callback is used in the context of the Button.
Besides, one of the main tenets of React is that "it's just JS", so normal JS rules apply. Now, the old-style `React.createClass()` method _did_ actually bind all "class methods" to the component instances so you didn't have to worry about it, but that wasn't typical JS behavior. ES6 classes don't auto-bind their methods, so neither does the `React.Component` base class.
The current recommended approach is to use the Stage 3 Class Properties syntax, which allows defining class methods as arrow functions that are auto-bound to the proper class/component instance. Very long-term, the React team intends to introduce a "stateful functional component" API that doesn't have to worry about class instances, but that's going to be a while. (There are some assorted userland experiments with writing "`this`-less components" as well.)
That's a good question, I never thought of that. Maybe while rendering they don't have easy access to which component is "this" due to the recursive algorithm they probably use? That's my only guess.
This is the solution I've been using, but it's not ideal. The lambda stored in updateState is recreated every time the class is instantiated, which can create problems for the GC depending on how often React creates temporary instances of Foo for its internal purposes.
That's how classes work, the lambda doesn't change anything here. The GC deals with it without problems. If an instance of Foo is not used anymore, the GC will destroy it and anything bound to it.
ES6 methods are defined outside the constructor so are O(1), but all class-fields are assigned inside the constructor thus O(n), so it can make a difference.
That statement seems wrong, or maybe I'm having trouble interpreting the way you're phrasing things (particularly with your use of "O(1)" and "O(n)").
Standard ES6 class methods are defined on the _prototype_, so only one function reference exists per method.
Binding class methods per instance, either by hand or using the Stage 3 Class Properties syntax, would result in a separate function reference per method per class instance (ie, 4 bound methods on a component class, times 5 instances of that component, would be 20 method references in memory instead of 4).
So yes, binding methods technically has some perf overhead, but most apps are unlikely to be creating thousands of instances of any given component type. To me, this is not a realistic concern. (In fact, if you think about it, you're generating _lots_ of functions every time you re-render if you're calling something like `someArray.map(() => {})` - way more than you ever would just from instantiating some components.)
He's trying to illustrate that an expression returns a reference to the (unbound) function. This line "(a.c || [])()" is bothering me -- why on earth would this line exist in any program? Are we trying to throw a type error on purpose? Maybe "(a.c || (()=>{}))()" would have been more appropriate.
I spent about 5 minutes wondering why each line returned 1, 2 and 3 respectively, when they should be returning the global value of b. If you are going to be showing obscure, tricky examples of Javascript, the last thing you want to do is have superfluous comments in your code. Follow comment conventions for the sake of your readers' sanity. This would have been much less confusing:
It's really frustrating that the ECMA people didn't define class methods to always be bound just like arrow functions. Like arrow functions, classes are a new syntax so there are no backward compatibility concerns.
That said, having two different semantics based on which syntax you happened to use to define a function is almost as big a programmer trap as the original problem. It just goes to show there are no good solutions to fundamental language design errors.
>It's really frustrating that the ECMA people didn't define class methods to always be bound just like arrow functions.
This is a huge annoyance because I never really know where to bind my events. It always ends up just being an awkwardly placed "bindEvents" call somewhere in the init function.
I realize, but it's more of a reaction to the inanity of JS, moreover, since I've started using JS, I've never once had any issue with 'this' - the way one constructs code often in TS is simply quite different i.e. it's really not just 'JS with typing' even if you want it to be just that, usually it's not.
My TS code looks almost identical to my JS code, but with types. Some JS patterns are a little harder to express in TS, but only because they're inherently a little more difficult to implement correctly. Things like higher-order functions (and HOCs).
Blame? Not only is this how I want it, I think it's the obvious and natural progression of Typescript code.
Typescript is not just 'typing' for JS, it encourages much cleaner abstraction, modularization etc. than JS.
My first Typescript exposure was a re-write of several thousand lines of JS code. After a little bit of 'adding typing' I realized there were much better ways to organize information. The result was powerful - what was once a near hairball of code, was now magnificently cleaner, easier to read, easier to re-factor, easier to maintain.
That experience not only validates the existence of TS but strongly highlights the limitations of JS which has always been a deeply problematic language, full of pot holes and weirdness, and though it's fully understandable why the language evolved how it did - that's no justification for it's ugly failings which cause existential inefficiency in many situations.
Though 'adding typing to JS' might be a good start for using Typescript in any given scenario - anyone only doing that is missing a bigger opportunity.
The reason your posts in this thread are so irritating is because TS has all the same junk people hate about JS. You say things like JS is "deeply problematic" and "full of pot holes and weirdness" but you don't address how TS has fixed any of these problems. (Hint: it hasn't. Fixing the language is totally beyond the scope of TS).
Typescript provides concrete benefits. Maybe it also provide some benefits that aren't so concrete, like encouraging developers to think about how they're representing data earlier rather than later. But it doesn't "fix the language".
I agree TS definitely does not 'fix' JS, but in practice, it helps avoid many issues.
Example: prototype inheritance.
Now you can argue that's a 'good feature' of JS, but I'd argue it's just convoluted and difficult.
Since programming in TS ... I've basically never run into a prototype inheritance concern, and maybe a little bit daring (or lazy) to say that I've forgotten many of the details. You really don't have to know anything about prototype inheritance when you're in TS land.
Modules. Technically, that's going to be 'fixed' in a near future version of JS, but really, it's not. Modularization is cludge in JS with various versions. In TS it's fairly clear. No issues there either.
Also, annotations.
Anyhow, yes, you're right technically, but TS does obfuscate a lot of the ridiculousness to the point wherein I would say TS is borderline another layer of abstraction, not just 'types'.
It's the only thing in tech I actually talk up when I get the chance.
I think that's because TS lets you see patterns in your data and code that were "invisible" in JS. You could still write clean code in JS, you just have to be able to see all that implicit structure. Spaghetti code is spaghetti code with or without types, it's just easier without types.
Not knowing this 8 years ago in interviews was the same disqualifying as not knowing nowadays what is the latest Angular version and why is it incompatible with the previous version. The difference is that "this" is still relevant - just hit F12, enter "this", and hit <ENTER>.
If you're getting asked about Angular in your interviews, its probably because you applied to a front-end job where they explicitly told you that you will be using Angular 8 hours a day.
That's not to say that the design of "this" is perfect. And it doesn't help that JS' syntax was designed to resemble C-family languages that it differs from significantly. But nor it is some entirely arbitrary and illogical flaw. If you take the time, it's possible to gain an intuitive grasp of how functions and "this" work in JS. But instead, learners are too often told that "JS is quirky" by others who also have come to it from Java / C#, and never move past memorizing to a list of these "quirks" to genuine proficiency. They then grumble whenever they need to work on a JS codebase, because their incomplete understanding makes it feel like they're standing on quicksand.
Soon such devs fall into a circular trap: They don't like coding in JS because it feels weird and unintuitive. And they don't take the time to learn about it more deeply because they don't like coding with it, and don't want to waste time learning about something they dislike. But since they have to code with it, because it's part of their job, they just stumble along, hacking away and getting frustrated when things don't work, then come onto Hacker News and blow off steam about what a garbage language JS is.