I’m quite unfamiliar with OOP in JavaScript, so I’d appreciate if someone could chime in with how this differs from ES5: is this just syntactic sugar, or is there a semantic change as well?
It’s not quite only sugar, though it’s close, just as spread is almost purely sugar, but when applied to constructors can do things that were beforetimes impossible.
My favourite subtle distinction is that methods are not constructors—this is true of object shorthand as well.
You were taught that these two were equivalent?
var x = { foo() {} };
var x = { foo: function() {} };
Try this on each:
new x.foo
The second definition works, but the first fails because x.foo is not a constructor. This has actually caught me, in a codebase with an old-style Class() creator (and no, `class` is not quite a suitable replacement there, at this stage, for reasons I won’t go into). `Class({ init: function(){} })` would work fine, but `Class({ init() {} })` would yield an uninstantiable class—except that the result was being fed through Bublé, which reinstated the `: function` for old browsers’ sake. So it was only when I removed Bublé from the pipeline that it blew up!
(To demonstrate this same point in ES6 classes, `new (new class { foo() {} }).foo` will fail.)
> JavaScript classes, introduced in ECMAScript 2015, are primarily syntactical sugar over JavaScript's existing prototype-based inheritance. The class syntax does not introduce a new object-oriented inheritance model to JavaScript.
Do note though that there is a lot of syntax that is hidden with the `class` keyword. So even though it is primarily a syntactic sugar, it is by no means trivial.
Calling something “syntactical sugar” makes it sound unhealthy.
I found the ES6 classes to be very helpful in conceptualizing stuff. When I started playing with them I’ve built myself a demo https://codepen.io/ronilan/pen/PObzew as a clone of same done previously in Ruby. Then I found classes to be very useful for bigger “vanilla” projects (https://github.com/ronilan/BlockLike)
Important emphasis on the quotation marks around the word standard. From the readme:
> But this isn't a real web standard!
> Of course it's not! The style laid out here is not affiliated with any official web standards groups, which is why this repo is called standard/standard and not ECMA/standard.
> The word "standard" has more meanings than just "web standard" :-)
I’m not a fan of the naming, but I could also be biased because I disagree with some of the formatting rules.
Unfortunately in the JS community you cannot say these things without being punished. If you do anything else than strict eslint with "airbnb settings", or default prettier settings, your head goes off! Don't ever say this to your JS team mate. It's a very rigid community if it comes to these futilities.
This is... puzzling. Where do you see these attitudes? Both ways are perfectly cromulent and it's fine for people to have opinions.
In my years of JS dev the main thing I've seen from the community is "pick a style and stick to it. Be consistent." And that's pretty typical and sage advice of all programming.
The reason to downvote parent is that it's bringing up a tired debate. We may as well discuss tabs vs. spaces while we're at it.
> The reason to downvote parent is that it's bringing up a tired debate.
Do you mean that even when it is scientifically proved that your code is more readable when using more white space like in: console.log( "Hello world!" ); we are not even allowed to mention that because some random dudes decided we have to adhere to the "airbnb" settings?
I bring this up because I think it's pathetic, like many other things in the JS community. Tired debate or not, we should have freedom of thinking and speech please! Programming is also a very creative activity, we should not destroy that with these kind of silly rules that make it more of an administrative activity.
No, you’re free to talk about whatever you want. But don’t expect pointless bikeshedding to be well-received.
First, it’s not a real problem. The JS community is pretty much equivalent to any other of this matter.
Second, it’s not a big issue. Use a code formatter to make sure shared code is in the agreed style for whatever project you are contributing to.
Third, this does not affect your own code. You are free and encouraged to use whatever formatting convention works for your own teams. Just make sure it’s consistent and enforced.
Keeping consistent style is anything but a “silly rule” and it’s essentially no effort.
It is a real problem, I've seen heated discussions in a few companies, and it can take out a big part of the fun you have in your work.
We really need to write an editor plugin/solution for this. Your editor should show the code in your preferred style, but saving it in the team's agreed style. And then a local-style.js for your own settings, and a team-style.js for the team settings. local-style.js in the .gitignore. Should be possible though?
It'd be possible but it's a non-trivial amount of work: think about things like linting or debuggers where line numbers might vary substantially, sequencing things in tools around merges and code review, etc.
The option you can do right now is to use something like Prettier where everyone's experience is entirely consistent and you have no cognitive load switching styles or dealing with inconsistency. This is basically free and means that there's a whole slew of high-emotion discussions which you can stop having for the rest of your career, in favor of working on things which actually matter. The older I get, the more that looks like pure win.
If I remember correctly it used to be the case that every JavaScript repo came with it's own eccentric style. I'm certainly guilty of that. Now -- when we have these style "standard" style guides -- we've sort of settled on 3 or 4 competing standards. None of them look particularly bad (or at least not worse then my personal style cerca 2012).
Don't get me wrong. We still engage in stupid bikeshedding about semi vs. no semi, tabs vs. spaces etc. but I feel overall we've embiggened our spirit when it comes to code style debates.
I think ES6 classes are the worst of all worlds. I know people from a Java background really want classes in their Javascript but this is not that.
ES6 classes look like kind of sort of like a class but if you poke it at all you see lots of sharp edges and weird behaviours that make no sense unless you know what it's sugar for.
I think the point was to simplify the JS Prototype syntax to be more "normal-looking" and less confusing to devs, not some rich OOP model compared to true OOP languages.
I'm all in favour of simplifying some of the syntax but why call it a "class"? That's only going to further confuse matters. I already have a hard enough time trying to teach Javascript's OOP model without adding to the confusion.
Why not? It's totally reasonable if you learn a language to get familiar with the concepts in it. Every language is different here and there and the 'class' describes well what it does: wrap things together that belong together.
>and the 'class' describes well what it does: wrap things together that belong together.
So does "Object". All "Class" does, here is paper over some of the complexity of making JS objects.
If anything, JS classes hide necessary concepts about the language from the developer. At least with the old, ugly way of doing things, you were forced to confront prototypes and scope and how 'this' worked.
> I think the point was to simplify the JS Prototype syntax to be more "normal-looking" and less confusing to devs
So they bolted on an incompatible thing that hides prototypes and is confusing to devs:
class C {
y = 10;
// this fine. "methods" defined as class properties
// have access to proper `this` due to how class properties work
// and scoping rules
x = () => if(this.y > 10) this.y = 10;
constructor() {
// if you don't bind `this`, `method()` will not have access
// to `this`
this.method = this.method.bind(this)
}
method() {
// will refer to a wrong `y` if `this` isn't bound
// in the constructor
this.y++;
}
}
No, it's not addressed. And you can see me using class fields already in `x = () => ...` <- this is a class field (I called it class property, sorry).
The fact that arrow functions declared as class fields get access to the proper `this` in the class instance is just a happy(?) coincidence with its own downsides. For example, class fields can't be inherited in the child class. So this adds a yet another layer of confusion for devs.
And as you see in the example at the link you provided, you still need to `.bind(this)` the actual methods.
The `y = 10;` is a class field, which is part of a Stage 3 proposal, https://github.com/tc39/proposal-class-fields. It’s not yet finalised, though it’s getting there. No current browser supports it, and those that write their code thus depend on Babel or similar projects to transform the code into a property definition in the constructor. Chrome 72, the next release, introduces support for it. Decide for yourself whether all that counts as “perfectly legal JavaScript” or not!
Now onto bound methods: you are wrong in your code example; `instance.method()` does not require `method` to be a bound method; an unbound function is just fine. The code is equivalent to `instance.method.call(instance)`, which (for an unbound function) calls it with `instance` as its context. The problem is when, at call time, you don’t access the function as a property on another object, like this:
const fn = instance.method;
fn();
Or `(1, instance.method)()`. Then you need to worry about what the context will be (in strict mode, it’ll be `undefined`, and so any property access on it will immediately blow up, which is good).
Bound methods are created by Function.prototype.bind, or by arrow functions.
In what I will choose to call “normal code”, it’s somewhere between very rare and not particularly common to need to bind a function manually. Understand this: React is not normal code. React was designed in a distinctly unusual way (they invented an rather substantial language extension to achieve it!), and although that language and design has turned out quite well in most ways, it also has some exceptionally nasty weaknesses and rather annoying ergonomic troubles; and the need to bind functions is one of them. This is not JavaScript’s fault, but is because React has gone for a VDOM approach that (I think?) requires object identity in such cases, for reasons of efficiency. In search of one form of ergonomics, they damaged another form that others don’t have to worry about very often at all. Such is life, and the nature of tradeoffs.
Most of the trouble with needing bound functions happens with event handling. (Certainly not all, but most, in my experience.) In non-React, non-VDOM-based code, you might be doing something like this:
… and that’d be perfectly fine there. And a whole lot more efficient at runtime than React, I might add. * If you needed `this`, then the arrow function got it for you automatically.
Sometimes you would want a separate function to designate the event handler, and so you may want to call bind(), but it’s also just as likely that you’ll want to keep Event out of that method, and just do `foo.addEventListener('bar', event => this.handleBar(event.detail))`. Maybe, maybe not.
From your earlier example: be careful with your `x = () => …` instead of `x() { … }`, because that instantiates a new function for every instance of the object, which is very inefficient for memory consumption and performance. Binding a method from the prototype is much more efficient (though still to be avoided where unnecessary).
> Now onto bound methods: you are wrong in your code example; `instance.method()` does not require `method` to be a bound method; an unbound function is just fine.
Ah, true. I looked at the code, and was blind to it :)
> Understand this: React is not normal code. React was designed in a distinctly unusual way
There's nothing unusual about React.
> (they invented an rather substantial language extension to achieve it!)
That "extension" is nothing but function calls [1] [2]
Quote: "Each JSX element is just syntactic sugar for calling React.createElement(component, props, ...children). So, anything you can do with JSX can also be done with just plain JavaScript."
> This is not JavaScript’s fault
It is entirely Javascript's fault. That is, it's the fault of its haphazard development with reckless disregard to any long-term planning.
Classes are deliberately designed to look and behave, for the most part, like classes in C++-like languages. So they hid the whole prototype chain away behind the class keyword... But it's still there, and you have to be always consciously aware of it.
As for the rest of your example, and how "you would rarely need to bind methods", well even the class fields proposal binds methods in its examples. So much for "rare" [3] And with quite a few people using WebComponents which are also class-based, well... Just a literally the first example I found: [4]
And of course class fields will behave like class fields in C++-like languages will behave. Unlike methods. Because reasons.
And of course, the "solution" to that is to throw in another half-baked solution in the form of a decorator. Which will take another several years to go through the standardization stage. ¯\_(ツ)_/¯
Because that's how you initialize attributes of an object instance in JS, in January 2019.
I don't believe this code does what you think it does. Even if your definition of `x` generated an attribute of instances of C, using an arrow function would defeat your apparent intention: arrow functions take their surrounding lexical scope's `this`, which in this case is the class, not any instance you define from the class.
> If you don't bind `this` to `method` in the constructor, when you call `instance.method()`, `this` will be whatever (window, IIRC).
Simply not the case, as another commenter has pointed out: `instance` in your loop is definitely `method`'s `this`, and not `window`.
Ah, true. I keep forgetting class fields are not standardized yet since eve ryone is using them anyway just because they are so damn handy.
> Even if your definition of `x` generated an attribute of instances of C, using an arrow function would defeat your apparent intention: arrow functions take their surrounding lexical scope's `this`, which in this case is the class, not any instance you define from the class.
For the current implementations of class fields this works. If they change that behaviour, what good are classes?
> If they change [arrow functions taking `this` from lexical scope instead of caller], what good are classes?
Using an arrow function on a class field seems like a way to get static methods that are still callable from the instance. But I'm not sure I have a burning need for that at the moment...
I think ES6 classes were one of the best things added in ES6. Before then there were a half dozen idiomatic ways to make "classes". Now there's one main way. It's just syntactic sugar. They're not real classes, whatever real means. But they do their job just fine.
They're real enough? Smells like a class, tastes like a class...
I think it's unfortunate that classes now have more syntax conveniences than that for building prototypal object hierarchies. I find `Object.create(...)` and `Object.assign(...)` unclear to read and clunky.
As another aside: is there any reason to bother with JS constructors and "functional" OOP? I just use `Object.[create|assign](...)`, done.
It's "javascript classes" why should it 1:1 copy Java or other languages? Just for the sake of people coming from other language background I don't see it justified.
Do people complain about differences between C++, Java, C# classes?
I'm happy about the class syntax additions that came with ES6, especially the method definitions. It's increased my productivity (and happiness) when working with js.
My only complaint is having to manually bind methods in the constructor. I understand why it's necessary but it is tedious. I suppose the answer is to just use TypeScript but we are talking about ES6 here.
50 comments
[ 2.6 ms ] story [ 35.8 ms ] threadMy favourite subtle distinction is that methods are not constructors—this is true of object shorthand as well.
You were taught that these two were equivalent?
Try this on each: The second definition works, but the first fails because x.foo is not a constructor. This has actually caught me, in a codebase with an old-style Class() creator (and no, `class` is not quite a suitable replacement there, at this stage, for reasons I won’t go into). `Class({ init: function(){} })` would work fine, but `Class({ init() {} })` would yield an uninstantiable class—except that the result was being fed through Bublé, which reinstated the `: function` for old browsers’ sake. So it was only when I removed Bublé from the pipeline that it blew up!(To demonstrate this same point in ES6 classes, `new (new class { foo() {} }).foo` will fail.)
> JavaScript classes, introduced in ECMAScript 2015, are primarily syntactical sugar over JavaScript's existing prototype-based inheritance. The class syntax does not introduce a new object-oriented inheritance model to JavaScript.
[1]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
I found the ES6 classes to be very helpful in conceptualizing stuff. When I started playing with them I’ve built myself a demo https://codepen.io/ronilan/pen/PObzew as a clone of same done previously in Ruby. Then I found classes to be very useful for bigger “vanilla” projects (https://github.com/ronilan/BlockLike)
I’d call them “syntactical vitamins”.
I would argue that, more often than not, it is, and it becomes less healthy the more of it you have.
Javascript, for all its faults, is syntactically a simple language and it should have been kept that way.
It always seemed like an odd choice to me that would make people less likely to adopt it.
> But this isn't a real web standard!
> Of course it's not! The style laid out here is not affiliated with any official web standards groups, which is why this repo is called standard/standard and not ECMA/standard.
> The word "standard" has more meanings than just "web standard" :-)
I’m not a fan of the naming, but I could also be biased because I disagree with some of the formatting rules.
In my years of JS dev the main thing I've seen from the community is "pick a style and stick to it. Be consistent." And that's pretty typical and sage advice of all programming.
The reason to downvote parent is that it's bringing up a tired debate. We may as well discuss tabs vs. spaces while we're at it.
Do you mean that even when it is scientifically proved that your code is more readable when using more white space like in: console.log( "Hello world!" ); we are not even allowed to mention that because some random dudes decided we have to adhere to the "airbnb" settings?
I bring this up because I think it's pathetic, like many other things in the JS community. Tired debate or not, we should have freedom of thinking and speech please! Programming is also a very creative activity, we should not destroy that with these kind of silly rules that make it more of an administrative activity.
First, it’s not a real problem. The JS community is pretty much equivalent to any other of this matter.
Second, it’s not a big issue. Use a code formatter to make sure shared code is in the agreed style for whatever project you are contributing to.
Third, this does not affect your own code. You are free and encouraged to use whatever formatting convention works for your own teams. Just make sure it’s consistent and enforced.
Keeping consistent style is anything but a “silly rule” and it’s essentially no effort.
We really need to write an editor plugin/solution for this. Your editor should show the code in your preferred style, but saving it in the team's agreed style. And then a local-style.js for your own settings, and a team-style.js for the team settings. local-style.js in the .gitignore. Should be possible though?
The option you can do right now is to use something like Prettier where everyone's experience is entirely consistent and you have no cognitive load switching styles or dealing with inconsistency. This is basically free and means that there's a whole slew of high-emotion discussions which you can stop having for the rest of your career, in favor of working on things which actually matter. The older I get, the more that looks like pure win.
Don't get me wrong. We still engage in stupid bikeshedding about semi vs. no semi, tabs vs. spaces etc. but I feel overall we've embiggened our spirit when it comes to code style debates.
ES6 classes look like kind of sort of like a class but if you poke it at all you see lots of sharp edges and weird behaviours that make no sense unless you know what it's sugar for.
I'm all in favour of simplifying some of the syntax but why call it a "class"? That's only going to further confuse matters. I already have a hard enough time trying to teach Javascript's OOP model without adding to the confusion.
So does "Object". All "Class" does, here is paper over some of the complexity of making JS objects.
If anything, JS classes hide necessary concepts about the language from the developer. At least with the old, ugly way of doing things, you were forced to confront prototypes and scope and how 'this' worked.
So they bolted on an incompatible thing that hides prototypes and is confusing to devs:
And available now as: https://babeljs.io/docs/en/babel-plugin-proposal-class-prope...
The fact that arrow functions declared as class fields get access to the proper `this` in the class instance is just a happy(?) coincidence with its own downsides. For example, class fields can't be inherited in the child class. So this adds a yet another layer of confusion for devs.
And as you see in the example at the link you provided, you still need to `.bind(this)` the actual methods.
Don't believe me? Try a more complex example like React's event handling with `this.setState()`: https://reactjs.org/docs/handling-events.html
Now onto bound methods: you are wrong in your code example; `instance.method()` does not require `method` to be a bound method; an unbound function is just fine. The code is equivalent to `instance.method.call(instance)`, which (for an unbound function) calls it with `instance` as its context. The problem is when, at call time, you don’t access the function as a property on another object, like this:
Or `(1, instance.method)()`. Then you need to worry about what the context will be (in strict mode, it’ll be `undefined`, and so any property access on it will immediately blow up, which is good).Bound methods are created by Function.prototype.bind, or by arrow functions.
In what I will choose to call “normal code”, it’s somewhere between very rare and not particularly common to need to bind a function manually. Understand this: React is not normal code. React was designed in a distinctly unusual way (they invented an rather substantial language extension to achieve it!), and although that language and design has turned out quite well in most ways, it also has some exceptionally nasty weaknesses and rather annoying ergonomic troubles; and the need to bind functions is one of them. This is not JavaScript’s fault, but is because React has gone for a VDOM approach that (I think?) requires object identity in such cases, for reasons of efficiency. In search of one form of ergonomics, they damaged another form that others don’t have to worry about very often at all. Such is life, and the nature of tradeoffs.
Most of the trouble with needing bound functions happens with event handling. (Certainly not all, but most, in my experience.) In non-React, non-VDOM-based code, you might be doing something like this:
… and that’d be perfectly fine there. And a whole lot more efficient at runtime than React, I might add. * If you needed `this`, then the arrow function got it for you automatically.Sometimes you would want a separate function to designate the event handler, and so you may want to call bind(), but it’s also just as likely that you’ll want to keep Event out of that method, and just do `foo.addEventListener('bar', event => this.handleBar(event.detail))`. Maybe, maybe not.
From your earlier example: be careful with your `x = () => …` instead of `x() { … }`, because that instantiates a new function for every instance of the object, which is very inefficient for memory consumption and performance. Binding a method from the prototype is much more efficient (though still to be avoided where unnecessary).
The most interesting approach to resolving this binding problem that React causes lies in the decorators proposal (currently at stage 2). https://github.com/tc39/proposal-decorators/blob/master/boun... is all about a @bound decorator.
* Just be careful with adding event listeners, and remember to use bubbling.
Ah, true. I looked at the code, and was blind to it :)
> Understand this: React is not normal code. React was designed in a distinctly unusual way
There's nothing unusual about React.
> (they invented an rather substantial language extension to achieve it!)
That "extension" is nothing but function calls [1] [2]
Quote: "Each JSX element is just syntactic sugar for calling React.createElement(component, props, ...children). So, anything you can do with JSX can also be done with just plain JavaScript."
> This is not JavaScript’s fault
It is entirely Javascript's fault. That is, it's the fault of its haphazard development with reckless disregard to any long-term planning.
Classes are deliberately designed to look and behave, for the most part, like classes in C++-like languages. So they hid the whole prototype chain away behind the class keyword... But it's still there, and you have to be always consciously aware of it.
As for the rest of your example, and how "you would rarely need to bind methods", well even the class fields proposal binds methods in its examples. So much for "rare" [3] And with quite a few people using WebComponents which are also class-based, well... Just a literally the first example I found: [4]
And of course class fields will behave like class fields in C++-like languages will behave. Unlike methods. Because reasons.
And of course, the "solution" to that is to throw in another half-baked solution in the form of a decorator. Which will take another several years to go through the standardization stage. ¯\_(ツ)_/¯
[1] https://reactjs.org/docs/jsx-in-depth.html
[2] https://reactjs.org/docs/react-without-jsx.html
[3] https://github.com/tc39/proposal-class-fields
[4] https://github.com/elmsln/lrnwebcomponents/blob/master/eleme...
> Why would I put this.y = 10 in the constructor?
Because that's how you initialize attributes of an object instance in JS, in January 2019.
I don't believe this code does what you think it does. Even if your definition of `x` generated an attribute of instances of C, using an arrow function would defeat your apparent intention: arrow functions take their surrounding lexical scope's `this`, which in this case is the class, not any instance you define from the class.
> If you don't bind `this` to `method` in the constructor, when you call `instance.method()`, `this` will be whatever (window, IIRC).
Simply not the case, as another commenter has pointed out: `instance` in your loop is definitely `method`'s `this`, and not `window`.
Ah, true. I keep forgetting class fields are not standardized yet since eve ryone is using them anyway just because they are so damn handy.
> Even if your definition of `x` generated an attribute of instances of C, using an arrow function would defeat your apparent intention: arrow functions take their surrounding lexical scope's `this`, which in this case is the class, not any instance you define from the class.
For the current implementations of class fields this works. If they change that behaviour, what good are classes?
Using an arrow function on a class field seems like a way to get static methods that are still callable from the instance. But I'm not sure I have a burning need for that at the moment...
I think it's unfortunate that classes now have more syntax conveniences than that for building prototypal object hierarchies. I find `Object.create(...)` and `Object.assign(...)` unclear to read and clunky.
As another aside: is there any reason to bother with JS constructors and "functional" OOP? I just use `Object.[create|assign](...)`, done.
Do people complain about differences between C++, Java, C# classes?
Alternatively, the autobind decorator could help - though I’ve never used that myself.