Every time I think I understand Javascript, I read something about its prototypes and constructors and how 'new' and 'this' works and I am lost again.
The thing I like about classes is I vaguely understand what is going on and why, coming from Java and similar. So I am using them, together with Flow typechecking.
I am not saying the javascript prototype logic is bad, just that I never understood it, even when I work in Javascript for years.
Sometimes I understand, for a brief moment, "how", but never "why".
> Every time I think I understand Javascript, I read something about its prototypes and constructors and how 'new' and 'this' works and I am lost again.
Prototypes, constructors, and context are not that complicated; I encourage you to keep reading and trying things. You may be surprised how quickly you end up "getting" it.
> The thing I like about classes is I vaguely understand what is going on and why, coming from Java and similar.
No. The class keyword in JS is syntactic sugar around the same prototype stuff that already existed. It does not work like it does in Java, and you will not understand what it's doing unless you understand how the class keyword translates directly into manipulating the prototype chain.
Which, as above, is easy! But if there's one actual problem with the class keyword it's that it makes people who know Java think they understand JS inheritance.
The class keyword DOES NOT CHANGE YOUR CODE. If you didn't understand what was going on before you used the class keyword, you don't understand it after adding it in. (Conversely, if you actually do understand how the class keyword works, then rejoice, you now understand how prototypes work...)
>Which, as above, is easy! But if there's one actual problem with the class keyword it's that it makes people who know Java think they understand JS inheritance.
Which is totally fine, as for all intends and purposes the behavior is the same. You create new instances, and you call methods on them.
The "movable" context (this) will be a little surprising, but for everything else one can write JS for decades with ES6 classes and "OOP", without having to know how the prototypes, objects and functions they de-sugar to work.
> Which is totally fine, as for all intends and purposes the behavior is the same.
Nope :) They're apparently the same, which is not the same as "the same".
> The "movable" context (this) will be a little surprising, but for everything else one can write JS for decades with ES6 classes and "OOP", without having to know how the prototypes, objects and functions they de-sugar to work.
That's just the thing though - there isn't a "movable context", there is only binding. Binding usually happens implicitly, and most of the time things are implicitly bound to what you expect them to be if you're not aware it's happening.
... then comes the moment that you're in an instance's method, and `this` is pointing to an event instead of the object. Oh, boy.
In my experience that moment comes much more often than once every few decades.
Yeah, I always understand "how" from books like this, but not "why". Why is javascript like that?
More to the point, what is the reason for prototype-based inheritance, what is its benefit, what more can you do with it than with C++/java, where does it make life easier.
- 'this' is based on how you call something, not what it is attached to. It's possible to have a.fn and b.fn be the same function with 'this' referring to each object in turn when you call a.fn() and b.fn().
- Function.bind muddies the waters a little bit, but basically that creates a wrapper that forces 'this' to a particular object.
- a 'class' in js is a function with a prototype property pointing to a parent object / parent class instance (not a class)
- new fn() creates an object, assigns the prototype, and runs fn.call(obj)
- obj.prop walks the chain of prototypes attached to obj until it finds 'prop' on one of the instances
As to the why, the idea is that the core object is the function, not the class, and prototypes are a way of conveniently determining the context a function is executed in. There is no such thing as a class in js, only a class-like syntax to wrangle functions and objects.
> Is there a a reason to learn bind, where I can use arrow function, which is cleaner and more readable? (And probably does bind under the hood)
Off the top of my head, arrow functions come in handy when you're defining an "instance method" that you want to use as an event handler; you want `this` to refer to the instance, not the event. That's 95% of the time you'd use an explicit call to `bind` anyhow.
Another case to use `bind` would be a generic function that isn't specific to the type of object you're acting upon. I wrote up a short example on jsfiddle: https://jsfiddle.net/LyndsySimon/5zqtjd82/
In prototype inheritance, objects inherit from objects. This creates flexibility, since you can dynamically create a base object to inherit from. You can also easily augment base objects after they're inherited from. Class-based inheritance is less powerful in most languages due to the increased runtime rigidity of the class hierarchy. That's the theory. In practice everyone treats prototypes as classes.
If you don't understand prototypal inheritance in ES5, you don't use it, and you stick with simpler concepts that work in a clean and elegant way that doesn't depend on OO-like trappings cluttering up your code unnecessarily.
But with that "class" keyword sitting there, it looks vaguely familiar enough, and you just start programming as if it were Java, and your code turns into this complex morass without you even realizing it until the pain starts.
> Which is the problem with "class" in a nutshell.
Which is not new with Javascript in general. every single feature in javascript suffers from being strangely familiar while having a behavior specific to Javascript. Comparison operators, var declaration hoisting,function declaration hoisting, "this", "typeof" operators,type equality and coercion rules, and co, all require to actually learn how the language works. This isn't a very good argument against the class keyword. Yes, developers who use a language need to learn it before hand. It applies to all languages.
> The thing I like about classes is I vaguely understand what is going on [...] I am not saying the javascript prototype logic is bad, just that I never understood it, even when I work in Javascript for years.
But by using classes, you're still using prototypes, it just isn't as immediately apparent.
Sure, and by using classes in C++, you're still using tables of functions pointer like you can in C. The "not as immediately apparent" part is where the usability improvement comes from. It means you don't need to think about the machinery as much. You can work at a higher level of abstraction.
(Of course, the details of how the feature is designed determine how leaky the abstraction is and how often you actually get the luxury of not thinking about the machinery.)
I think understanding "this" in javascript is actually much easier if you have had exposure to C++ function tables.
With that knowledge in hand, it immediately becomes obvious that:
bar.baz = function(){console.log(this.X)}
var foo = bar.baz;
foo();
Is not going to result in a console log of bar.X. "foo" is a pointer-to-member function, and executing it without a context is going to cause problems. The biggest in javascript as compared to c++ is that javascript will always provide the hidden "this" (even if it's the global context), whereas c++ will always bail out if you don't provide it with a proper context.
But really, understanding that behind the scenes, member function calls change:
A.B(C)
into
B(A, C)
clears up a lot of "this" mistakes, and also enlightens the proper usage of function.bind.
The thing I like about classes is I vaguely understand what is going on and why, coming from Java and similar. So I am using them, together with Flow typechecking.
I am not saying the javascript prototype logic is bad, just that I never understood it, even when I work in Javascript for years.
Sometimes I understand, for a brief moment, "how", but never "why".