JavaScript: prototype vs. class
medium.com
medium.com
For a simple es2015 it looks a bit more complicated than the prototype extension [2].
class Dog {
bark() { console.log( "Bark!" ); }
}
Compiles to : "use strict";
var _createClass = (function() {
function defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
return function(Constructor, protoProps, staticProps) {
if (protoProps) defineProperties(Constructor.prototype, protoProps);
if (staticProps) defineProperties(Constructor, staticProps);
return Constructor;
};
})();
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);
}
_createClass(Dog, [
{
key: "bark",
value: function bark() {
console.log("Bark!");
}
}
]);
return Dog;
})();
1 : https://babeljs.io/repl/2 : https://babeljs.io/repl/#?babili=false&browsers=&build=&buil...
"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;
})();
https://babeljs.io/repl/#?babili=false&browsers=&build=&buil...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.
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.
https://www.typescriptlang.org/play/index.html#src=class%20D...
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.
class Foo {
Bar(){}
}
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.
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.
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`...
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.
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.
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.
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.
But whenever i start looking on the details of how it actually works, I feel like my brain is experiencing a bunch of small seizures.
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.
Which are? And why is "no new" a benefit?
IMO it's a good thing that you can write JS in the way you write other languages. It makes it a lot easier for developers to get going with it.
I've gone from learning one way of doing things to two, how is that easier to get going with?
Also, prototypical classes like `function User` are now rare in my experience going forward.
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.
https://www.gnu.org/software/easejs/manual/easejs.html#Imple...
I also wrote a paper on some of the concepts:
https://mikegerwitz.com/papers/coope/coope.pdf
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.
No it allows "super" late binding, something you cannot do with functions and prototypes. It's not just "sugar".
[like this](https://babeljs.io/repl/#?babili=false&browsers=&build=&buil...)
Sounds like Javascript dev who is pissed because the language evolves and his prototype-hack-knowledge becomes more and more obsolete.
I'm not dissing it, that's great it does this. But I'm a bit surprised (and happy!) that this is all it does.
- no more messing with .constructor, .prototype
- calling parent object is super() easy :)
- you can have static methods
- no need to even type function for methods
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
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); Foo.prototype.constructor = Foo;
...after replacing the prototype object that way. MDN (for example) covers this in a few pages, including:https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Ob...
That said, it's a pretty common bug, particularly with stuff like:
Foo.prototype = Object.create(FooParent.prototype) // Crockford-style inheritance
console.log(Foo.prototype.constructor.name) // "FooParent"IMO create a class in Javascript and adding a static method is the opposite of simplify things.
The simple way of doing it would be create a function in a module.
For me the use of static methods in languages like Java is a workaround. But it Javascript that workarount is not necessary.
>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.For someone that has learned JS as his/her first programming language, the new syntax is the one that feels confusing.
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.
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.
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.
function Cat() {
if (!(this instanceof Cat)) {
throw new Error("please use new!");
}
}
or, if you want to be a bit nicer to your users: function Cat() {
if (!(this instanceof Cat)) {
return new Cat();
}
} function CatDog () {
Cat.apply(this, arguments);
}
CatDog.prototype = Object.assign(Dog.prototype, Cat.prototype);
In the above example, CatDog is not an instance of Cat, since it never actually inherits from the Cat prototype.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);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.