Javascript Fundamentals: ‘this’ keyword
blog.bloomca.me
blog.bloomca.me
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.
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.
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.
Saying that as a natural js hater.
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.Python. It's complicated®.
class Foo:
def bar(self, x):
print 'bar', x
def f(x): print 'f', x
foo = Foo()
foo.baz = f
print(foo.bar) # <bound method Foo.bar of <__main__.Foo object>>
print(foo.baz) # <function f>
foo.bar(5) # bar 5
foo.baz(6) # f 6
Now who wants to say that 'js this' is quirky and complicated?I really don't blame programmers for getting confused by this.
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.
'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.
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.
This is trivial. Don't create a problem that doesn't exist.
See this post by Ryan Florence that explains why "arrow functions in render" is not a big deal:
https://cdb.reacttraining.com/react-inline-functions-and-per...
<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.)
Here is a simple example:
class Foo extends Component {
state = { foo: 1 }
updateState = foo => this.setState({ foo })
render() { return <FooChild foo={this.state.foo} updateState={this.updateState} /> }
}
Foo and FooChild will render again only if 'foo' is updated.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.)
(that’s not always true, nor a complete definition - but it helps me a lot in understanding the whole thing)
const a = {
b: 42,
c: function() {
return this.b;
}
};
(a.c || [])(); // 1
(a.c)(); // 2
(1, a.c)(); // 3
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:
const a = {
b: 42,
c: function() {
return this.b;
}
};
(a.c || (() => {}))(); // undefined
(a.c)(); // undefined
(1, a.c)(); // undefinedA variation of it is valid for method selection, such as:
(cond ? a.foo : b.bar)();
or, for method fallback: (a.foo || a.fooDefault)();
Neither does work in js, though.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.
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.
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.
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.
> front-end job where they explicitly told you that you will be using Angular 8 hours a day