It's worth using Babel's loose mode (es2015-loose in the online REPL) if you're more interested in the "old" way of doing it than in compatibility with the specs:
"use strict";
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
var Dog = (function() {
function Dog() {
_classCallCheck(this, Dog);
}
Dog.prototype.bark = function bark() {
console.log("Bark!");
};
return Dog;
})();
> For a simple es2015 it looks a bit more complicated than the prototype extension [2].
That's because it provides somewhat different features and Babel apparently attempts to replicate them properly. For instance "class" methods are non-enumerable by default, whereas "prototype" methods — being bog-standard properties — are.
I sometimes see difference between TypeScript's compiler and babel.
For example in this case TypeScript doesn't care so much about `defineProperty`. I guess the rules are not so strict, since it assumes that the rest of your code will be passed through TypeScript itself.
Anyway in both cases, I prefer TypeScript's way of enhancing the class inheritance in JavaScript by introducing private / readonly and many more.
While it is good to understand how Javascript prototype inheritance works, I find it is best avoided , along with `this` and `class` syntactic sugar).
It is rarely required and most anything can be achieved with very basic JS objects and functions which are simpler to work with, easier to reason about and easier to write tests for.
and a list of methods is in my opinion more readable than
function Foo{}
Foo.prototype.Bar =function(){}
As for "avoid this". Really? this is part of Javascript, developers should understand it instead of "avoiding" it.
> It is rarely required and most anything can be achieved with very basic JS objects and functions which are simpler to work with, easier to reason about and easier to write tests for.
This is straight out false. It's no harder to "reason about" or "to write tests for". I get the impression that a tiny group of people are trying to push these ideas desperately for whatever reason, but it makes no sense, you're not the custodians of the language.
He's not telling people to not learn things or advocated changes to the language. Nor is he advocating the code you strawmanned above. He's saying he's found that `this` and `class` are best avoided. And I agree. I also agree that it's easier to test and reason about functional/procedural code or, more generally, code with fewer side effects and implicit state than OO code.
People usually share these thoughts, not out of desperation or ulterior motive, but because they've had experience with many paradigms and they've found these the most effective.
> He's not telling people to not learn things or advocated changes to the language.
That's something he should be explaining himself instead of giving random advices about what he considers "good practices". Neither you or him did elaborate nor give justifications to your beliefs. And that's the biggest issues. According to my experience, there is nothing wrong with `class` and or `this`.
These are features a Javascript developer should be comfortable with or they are not Javascript developers, because they are used everywhere in the JS ecosystem, including DOM api.
It's very telling when you people never elaborate on what's wrong with `class` and `this` and just seem to repeat ready made talking points about what's `considered harmful`...
If it's not clear, the issue isn't about the keywords, which are clearly an improvement compared to ES5. The issue is about doing OOP in JS. Much has already been written about OOP vs functional/procedural so I'm not going to rehash that. The OP's point is that you don't have to write OOP code in JS and that he believes JS code is cleaner without it. I think it's a good point to bring up because many developers have difficultly imagining non-OOP code and bring in patterns from C++/C#/Java because it's what they know.
I prefer OOP in JS and I have plenty of experience with functional JS. OOP code is more readable since functions have context and easier to reason about since there is way less indirection.
I wouldn’t say that apps written with functional style JS are cleaner at all.
There's like at least 3 or 4 different ways of constructing "classes" without the Class keyword. Frankly, that's a mess. I get that each has it's own advantages etc, but I don't WANT to figure out some hipster's clever new way of managing inheritance chains.
I want to use the supported method of creating classes within the language. I prefer usage of the "Class" keyword because no matter how the language changes from here on out, it's a pretty safe bet that "Class" is going to always be the rightest way to do it, and the one that everyone else will understand.
He's saying to prefer not using any version of classes, including the "hipster" alternatives to the class keyword, not just the class keyword itself. And I agree with him. But if I find the right use for classes, I enjoy using them. He was advocating for FP over OOP.
Work with D3.js and try to avoid "this". You'll be basically writing your own SVG manipulation library.
Avoiding prototypes and classes, means you've got no normalized objects. Which, I suppose works, if you're more procedural and less object oriented. But, in most of the projects I've worked on, it'd be a hindrance far greater than simply understanding the caveats.
I'm at about four years of JS development and I really haven't ran into issues like this. Maybe it's my lack of experience.
Code smell describes code which has incurred technical debts among other things. People do not have it.
The "latest language paradigms" in this case are JavaScript adopting a style of programming 40+ years old (Simula) -- despite its originating model being younger (self).
Adopting paradigms because they are "recent additions" to a language is cargo cultism.
Paradigms come with idioms that suit some problems better than others, and have all sorts of trade-offs and considerations.
Inheritance is now widely regarded as a design approach, overall, best avoided.
"easy to reason about" =/= hard to understand. The ease of reasoning about something is a feature of how complicated it makes it (partly, its incidental complexity). It's not to do with how dumb you are.
Inhertiance, for example, creates systems that are often needlessly hard to reason about.
We had a whole generation of class-heavy inheritance-ridden frameworks and applications. It's bad enough in static languages. In JS, it quickly becomes an unmaintainable pile of spaghetti.
Object literals with small doses (read: 1-2 levels at most) can be good. Going further risks multi-level breakage because properties are bound to get added/removed across the codebase over time due to the dynamic nature of JS.
I don't know how you avoid `this` and `class` though, especially with all client side code heavily relying on these.
I don't know about you, but coding in mid 2000s where these things didn't exist was toothache inducing levels of pain. At least `class` syntactic sugar standardizes how things look.
Just a cursory look at how an entire react would be written without class already sends shivers down my spine.
This doesn't do a good job of supporting its thesis. The new syntax does in fact allow most devs who understand "traditional" class based inheritance to use JS the way they expect. The fact that it's implemented in JS is both good and also OK to ignore for most devs.
In my experience, the class syntax has been more bad than good. Devs with experience in other languages simply start writing classical OOP code without realizing the ramifications of what they are doing.
Unless you are creating thousands of rigidly structured instances (never adding/removing properties or changing property types), you are almost always going to be better off using the factory pattern instead where you get other benefits like no `new` and real privacy.
I'm trying to learn modern JavaScript at the moment, as well as getting to grips with prototypes I now have additional class syntax to learn and understand.
I've gone from learning one way of doing things to two, how is that easier to get going with?
> Because now there's one way instead of everyone bringing their own
In theory. In reality there's everyone bringing their own plus one more.
I think the appeal of JS is that it's a universal runtime, not anything about the language itself. I really wish syntactic changes would be left to transpilation.. because that's what every JS developer is doing anyways.
Those interested in how prototypes and classical inheritance relate (and don't) may be interested in work I did on GNU ease.js, which works with ECMAScript 3+. I have since extended it to support Scala-like traits.
The `class' keyword in JS still leaves much to be desired; it's just syntatic sugar around the prototype model. There's nothing wrong with that model---it's just important to understand how it differs from what OOP developers traditionally expect.
This article didn't demonstrate any risks involved in using 'class' in Javascript by devs familiar with other OOP languages. What exactly is the non-common feature in Javascript OOP that exists in other OOP languages and therefore poses such a risk that makes developes make "a lot of mistakes"? It's not messing around with prototypes, since those devs don't understand that any way.
Sounds like Javascript dev who is pissed because the language evolves and his prototype-hack-knowledge becomes more and more obsolete.
You should never be using `.constructor` anyway because it isn't reliable because a very common pattern replaces the `.prototype` with a new object who's `.constructor` is `Object`
var Foo = function () {};
Foo.prototype = {
bar() { return 'bar'; }
};
var f = new Foo();
f.constructor === Object;
When you use `class` you are still messing with `.prototype`. The danger is that programmers new to JS don't understand that they are and don't understand why that matters.
Calling `super` is very anti-pattern in JS. Unlike classic OOP, parents are not static. Lots of very common things can rip that rug out from under you by modifying the parent in many different ways (createProperty, Symbols, proxies/reflection, etc).
Static methods do indeed exist without classes.
class Foo {
static bar() { return 'bar'; }
}
//is identical to
function Foo() {}
Foo.bar = function() { return 'bar'; }
If you use `Object.create()` and ES6 object literal syntax you don't have to type `function` either. You get the added bonus of being easily able to wrap it in a normal function that avoids constructor weirdness
var someProto = {
bar() { return 'bar'; }
}
var myInst = Object.create(someProto);
I'm actually a fan of the class syntax, and use it regularly.
>no need to even type function for methods
However, to be fair, you can do this with object method shorthand now:
const deepThought = {
theAnswer: 42,
tellTheAnswer() {
return `The answer to life, the universe, and everything is ${this.theAnswer}`;
},
};
deepThought.tellTheAnswer(); // => 'The answer to life, the universe, and everything is 42.'
Which is also quite elegant. I think they both have their places depending on the program. I think it depends on readability needs at the time, and whether an instance is better than just a container in that circumstance.
Is it really simpler? Let's say you take two equal students with zero knowledge of programming and teach classical to one and prototypical to the other. Would the student learning classical inheritance grok it faster? I doubt it.
I do agree that it is easier to understand the new syntax if you have experience with a language that use the same syntax. However, that's not learning anything new. It's simply using concepts that you already know.
Prototypical inheritance may seem simpler at first glance. But understanding how to use it correctly can be extremely difficult, even for experienced JavaScript developers, mostly because there are so many different ways to achieve almost the same thing, but with subtle differences. For example, you can reuse a single object instance as a prototype for multiple others. Or you can create a new instance of the prototype for each instance of the child. The two are similar, but affect e.g. whether or not properties in the prototype are shared or unique.
In the past I've eventually resorted to copy and paste the same template every time I wanted to use inheritance with prototypes. With classes, I never have to think about these problems anymore. There is one way to define a class, and it always works like I expect it to.
But that's the metric everyone uses to gauge a language.
Look at all the complaining about Lisp and Rust syntax which boils down to "ugh, this is unfamiliar."
But to answer your question, I think they are both within a pebble toss distance of each other. But still much harder than other languages due to `this`. `class` just unifies some patterns people were doing like inheritance.
Mixins have terrible performance characteristics and lead to fragile code. While they were huge a few years ago, most new frameworks either avoid them or (in cases like React) chose to remove them for these reasons. In an existing codebase, that matters a lot. In newer codebases (or newer parts of existing ones) it shouldn't matter that much at all.
Exactly. It's surprisingly well thought through. Literally the best of both worlds: it's not some entirely separate "new way" that's incompatible with the old way and adds significant implementation baggage, but still makes the most common in-the-wild use of prototypes shorter, while also being intuitive for non-Javascripters, and still allowing experienced old-school Javascripters access to the same prototype chain they're used to.
They are well thought through until you add class fields into the mix (already at stage three) and the actual real world usage (where you have to do this.method.bind(this) for every methods used in per-instance callbacks)
Agree on class fields but there's high demand. I haven't examined the exact detail but it seems hard to please both sides here. Not sure what the best way forward is.
On .bind(this), that's exactly what I meant by keeping the old system. "Fixing" that magically would have required underlying changes. That said, since you brought up class fields and bind in the same post, there's always:
class Dog {
bark() { console.log('Bark!'); }
eat = (food) => console.log('Munch');
}
feedDog(dog.eat);
My favourite solution so far is the @autobind decorator. It's far from native yet, though. And of course, enters the newer decorators feature which drifts ever closer to Java-like syntax to some peoples' chagrin.
Well writing code is much simplier this, but devs that are familiar with 'classical' OOP languages will do a lot of mistakes because of what is in the article.
A lot of these examples seem rather contrived, and the whole appendix has a very FUDy feel to it. It seems like the author didn't like classes in JS, and set-out to come up with some examples to justify their dislike, rather than coming to that conclusion through actually encountering these problems in the real world. Something not helped by the weaselly tone, constantly implying that it's just raising concerns, when it's clearly been written by somebody with an extremely strong anti-classes mindset.
As a simple way to define a reusable bundle of behaviour of logic, or to construct simply base-class -> specialisation inheritance hierarchy, JS classes work fine.
> If you change/replace a method (on purpose or by accident) on the parent "class", the child "class" and/or instances will still be "affected"
I knew when reading this line exactly what the author was about to do to mislead the unsuspecting user. Directly edit the prototype and feign surprise. The prototype is representative of the class itself, basically a default of all properties for that class. The correct (and obvious) way to override a class method for only one member of the class is to assign a new function on the property directly, not into the prototype's version of the property. Class definitions being mutated in an application is extremely uncommon. This is like putting your hand on a hot stovetop and then complaining that you burnt yourself.
For a simple es2015 it looks a bit more complicated than the prototype extension [2].
Compiles to : 1 : https://babeljs.io/repl/2 : https://babeljs.io/repl/#?babili=false&browsers=&build=&buil...