Live data from Hacker News

ES6 Classes

javascriptjanuary.com

41–50 of 51 posts

Re: ES6 Classes

#41

Earlier quoted context omitted.

You don't have to bind 'method' in the constructor to access 'y'. Just put 'this.y = 10;' in the constructor. Your example isn't legal JavaScript.

It's perfectly legal Javascript. Why would I put this.y = 10 in the cinstructor? Do you even understand what this code does? const instance = new C(); for(let i = 0; i If you don't bind `this` to `method` in the constructor, when you call `instance.method()`, `this` will be whatever (window, IIRC). Don't believe me? Try a more complex example like React's event handling with `this.setState()`: https://reactjs.org/doc…

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:

  const foo = document.createElement('foo');
  foo.addEventListener('bar', event => {
    …
  });
… 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.

Re: ES6 Classes

#42

No- static isDog (animal) { return Object.getPrototypeOf(animal).constructor.name === this.name; } Yes- static isDog (animal) { return animal instanceof Dog; }

Or, if you want to check “is a Dog and not an instance of some subtype of it”:

    static isDog (animal) {
        return Object.getPrototypeOf(animal).constructor === this;
    }
Definitely no need for the `.name` check—that weakens it enormously, especially in the presence of minification.

Re: ES6 Classes

#43
post #14

Earlier quoted context omitted.

From MDN[1]: > 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...

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 ) I’d call them “syntactical vitamins”.

> Calling something “syntactical sugar” makes it sound unhealthy.

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.

Re: ES6 Classes

#44

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 just syntactic sugar. Still using prototypal inheritance under the hood.

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.)

Re: ES6 Classes

#45
post #15

Earlier quoted context omitted.

Javascript is a true OOP language. 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.

Re: ES6 Classes

#46

Earlier quoted context omitted.

You don't have to bind 'method' in the constructor to access 'y'. Just put 'this.y = 10;' in the constructor. Your example isn't legal JavaScript.

It's perfectly legal Javascript. Why would I put this.y = 10 in the cinstructor? Do you even understand what this code does? const instance = new C(); for(let i = 0; i If you don't bind `this` to `method` in the constructor, when you call `instance.method()`, `this` will be whatever (window, IIRC). Don't believe me? Try a more complex example like React's event handling with `this.setState()`: https://reactjs.org/doc…

It is not legal JS as of January 2019.

> 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`.

Re: ES6 Classes

#47

Earlier quoted context omitted.

It's perfectly legal Javascript. Why would I put this.y = 10 in the cinstructor? Do you even understand what this code does? const instance = new C(); for(let i = 0; i If you don't bind `this` to `method` in the constructor, when you call `instance.method()`, `this` will be whatever (window, IIRC). Don't believe me? Try a more complex example like React's event handling with `this.setState()`: https://reactjs.org/doc…

It is not legal JS as of January 2019. > 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 sco…

> It is not legal JS as of January 2019.

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?

Re: ES6 Classes

#48

Earlier quoted context omitted.

It's perfectly legal Javascript. Why would I put this.y = 10 in the cinstructor? Do you even understand what this code does? const instance = new C(); for(let i = 0; i If you don't bind `this` to `method` in the constructor, when you call `instance.method()`, `this` will be whatever (window, IIRC). Don't believe me? Try a more complex example like React's event handling with `this.setState()`: https://reactjs.org/doc…

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 wh…

> 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. ¯\_(ツ)_/¯

[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...

Re: ES6 Classes

#49

Earlier quoted context omitted.

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.

Have you looked at using arrow functions in class properties for this? They have some issues, but avoid all the tedious manual binding. Alternatively, the autobind decorator could help - though I’ve never used that myself.

I've used Autobind a great deal as of recent with React classes, it's been both pleasant to use and I've yet to experience any issues with it.

Re: ES6 Classes

#50
post #36

Earlier quoted context omitted.

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 e…

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…

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.

Post reply on HN