Hacker News new | past | comments | ask | show | jobs | submit login

There are some nice ideas here, but how many different ways do you need to do something? Since JavaScript has an especially high burden for backward compatibility you can’t really remove old cruft, and adding yet another way to (whatever) just makes the language harder to learn and harder to read.

Warts and all, one of the best things about ES5 and earlier is that the language was so simple that you could pick it up in a day.

The later additions have added developer convenience for sure, but at the expense of taking something simple, consistent, and very easy to read, and turning it into a complex and subtle language with a lot of redundant syntax. For example all the different syntaxes for defining a function with the subtle differences between them. And in this articles proposal - since switch doesn’t do what I want, we’ll have both switch AND case with different semantics and syntax. Ugh.

At this point I’d rather see an effort to deprecate cruft or standardize features over throwing in new things.




There was nothing easy to read about the callback hell that resulted in the asynchronous nature of JavaScript.

Promises made it better but that was still verbose. Then async/await was added that made code read more linearly.

There was also the unintuitiveness of how var declarations were always function scoped compared to how most developers would think that variables should be blocked scoped using let and const.

Also I understand how prototypical inheritance works and I know that the class syntax is just syntactic sugar, but it is a lot more readable.


Improving some of the syntax was sorely needed but I think adding classes was unhelpful. I already have a hard enough time teaching programmers about prototypical inheritance without the language actively lying to them.

And it feels like an odd move in that class based OOP isn't the zeitgeist it once was. Shoehorning it in feels like a throw back to 90's and 00's.


> I already have a hard enough time teaching programmers about prototypical inheritance without the language actively lying to them.

One way to look at it is that you could consider not teaching the underlying prototypal inheritance model at all. If students will rarely run into code that relies on it, it may be vestigial semantics.

Think of it like how people used to teach patterns in C for doing object-oriented programming. For example, you could have a struct whose first field was a pointer to a table of functions for the operations you could perform on the object. In other words, how to implement your own vtables from scratch.

When C++ came along, it was still useful to understand vtables sometimes, but it wasn't something every programmer needed to fully internalize. C++ let them program at a higher level of abstraction.

> And it feels like an odd move in that class based OOP isn't the zeitgeist it once was.

Citation needed. :) Two of the newest, fastest growing languages are class-based: Kotlin and Swift. I'm not aware of any new prototype-based language that has noticeable growth.

Prototypes are a neat idea, but I think the market has pretty clearly shown that classes are a better practical approach to OOP.


I'm skeptical that classes make prototypal inheritance safe to ignore, because they're not a perfect abstraction. Opinion me, classes in Javascript do not act like classes in other langagues.

In particular, I still run into usage of `call` and `apply` on a regular basis.

  class Foo {
     constructor () { this.foo = 0; }
     log () { return this.foo; }
   }

  (new Foo()).log.call({ foo: 2 });
I guess I can explain the above without prototype, but it's certainly weird behavior for a classical OOP language, and at the very least it's dancing around prototype-like behavior.

Or, much worse:

  class Bar { constructor () { return new Foo(); } }
And you can say, "how often is a student ever going to run into that case", but part of being a student is experimenting with code. If an abstraction only holds as long as you never do something weird, then it's probably not a very good abstraction. In that case it's worth asking, "is it good language design to have a feature give surprising results as soon as you do something that's even slightly off of the beaten path?"


Well, in that case the abstraction of private methods isn’t good either because there are ways to bypass them in C++ by passing a function pointer or in C# via reflection.


Arguably, yes.

However, I would say there's some difference in severity between a surprising behavior that results from the intersection between two features, and a surprising behavior that results because the way a feature is described is literally inaccurate.

You can break Javascript classes without using any other feature of the language. You don't have to resort to something like Proxies, or overriding native prototypes. This is because it's not that other parts of Javascript provide escape hatches to get around behavior, it's that the class syntax itself does not cover the entirety of the prototype system it is trying to obscure.

It's not ideal to have any breakage in an abstraction, but some breaks are worse than others. To me, edge cases like this:

  let foo = function () { this.x = 5; }
  class Bar extends foo { //No error? But why?
    constructor () { super(); }
  }

  foo = function () { this.x = 7; }
  console.log((new Bar()).x); // 5, not 7 or undefined.
are on a whole nother level of leaky abstractions over even some of the worst parts of C#. To me, this is an edge case that nearly any reasonably imaginative student would likely find at some point using only very basic components of the Javascript language. And I have no idea how I would even begin to tackle explaining this without saying, "Okay, throw out everything I taught you about classes, here's how the language really works."


I don't know why prototypes have to be required for explaining those two examples. 1: You can call a function with a different `this` value. 2: A constructor can override its return value and return something else besides the new instance of its class.


> You can call a function with a different `this` value.

To understand what that means, you need to understand what `this` is in Javascript -- that classes don't actually bind methods to an instance. So we have this idea of methods from other languages that turn out to be kind of the wrong way of thinking about things.

From `this`, the obvious next question is, "so what's the difference between adding a method to a class and just attaching it to an object?" And it turns out that practically, the difference is very small -- and in fact it's very easy for us to remove or add methods to a class that look and act very similarly to the methods in a class descriptor. So what we're calling a "class" is no longer a descriptor or a set of laws about what an instance is and what it can do, it's just a blueprint of how to build an instance at runtime.

We haven't gotten rid of classes yet, but we're already working with something that feels foreign to a number of programmers coming out of traditional OOP languages. `this` means that what a traditional OOP programmer from Java thinks of as a "method" doesn't exist. It means that the context of a function is determined at runtime, not at compile time.

You don't need to use the word "prototype" to talk about that, but it's what I would call very prototype-ish behavior. Arguably, by the time someone understands `this`, they've already learned about half of what they need to know to grok prototypes anyway -- all that's left is to tell them that if a property isn't found on an object, there's a special property JS will traverse to look for it elsewhere. But sure, we can delay talking about that for right now.

> A constructor can override its return value and return something else besides the new instance of its class.

Constructors complicate things more. We already know from above that a class isn't really a descriptor of what an object can do, instead we're thinking about it like a blueprint that tells us how to create an object at runtime (this is also incorrect, but we're sticking with that definition because we don't want to use the word "prototype"). Now we find out we can override the return value of a constructor. The obvious question a good student will ask is, "why? What use-case is there for treating a constructor function like a normal function?"

Again, we don't have to jump to prototype from here, but we do kind of need to explain that the `new` keyword is actually doing most of the work when we build a class, and the reason we can do weird things with a constructor is because a constructor is just another function. But we'll avoid prototype by saying that `new` just makes an instance of a class, and that behavior can be overridden by returning another object from the constructor that `new` invokes.

And when the student figures out that they can call `new` on an anonymous function without returning anything and it still creates an object, we can delay talking about prototype even farther, because we'll just lie and say that pure functions are a shorthand for making classes that are only a constructor.

But increasingly the question becomes, why wouldn't we talk about prototype now? Ostensibly, the point of classes is that it hides all the weirdness of the prototype system. In my experience, people struggle with prototypes because they're not Java. But JS classes also aren't Java, or at least they stop being Java as soon as you do anything even mildly interesting with them. So if people who learn classes in depth still have to grapple with the fact that class definitions are constructed at runtime, and that instance methods can be modified after they're created, and that methods aren't inherently bound to instances at all, what have we gained?


I agree that prototypes come into play if you start asking "why did they add this feature?" to either of those questions, or if you want to explain why JS behaves the way it does if you go off the paved path ("And when the student figures out that they can call `new` on an anonymous function without returning anything and it still creates an object..."). I think I disagree that either of those are on the critical path to becoming productive with a language.

I just think people really over-emphasize the difference between "class-based" and "prototype-based" languages as if they're completely different in how users code with them. In my experience, the primary interesting consequences of JS's prototype-basedness vs class-based languages is that you can call methods with arbitrary `this` values, monkey-patch classes at runtime, or even change the class of an object at runtime. In other languages, these kinds of features would be called reflection, they would be in a late chapter labeled "advanced features", and people wouldn't worry about teaching them to students before they had a good handle on the rest of the language.


I do not use the "class" command in JavaScript (although I know it just does prototype inheritance), and think it was unnecessary to add (but you can't really remove it now).

I think prototype inheritance is often more useful than class inheritance; I do not agree that classes are necessarily better. Prototype inheritance can be used for more kind of things (especially with Object.create and Object.getPrototypeOf; I sometimes use these).


I don’t disagree that prototype is rare in real world practice but, anecdotally, it has come up in ~20% of my interviews in some fashion. That’s enough that I would be upset if the person teaching me JavaScript didn’t also teach me prototypal inheritance.


I only bring up the prototype to determine if I would classify the interviewee as a senior JavaScript developer or not.


In modern times that's something you can easily throw far back in mindbank, because in practice using ES6 it almost never comes up, and it's super easy to do a quick refresher on the semantics. If I didn't know that interviewers tended to ask about it in frontend inteviews (because they totally do), would never bother to keep up to date with the semantics


Object.defineProperty('senior', { value: true, enumerable: false })


There are two things I would expect even a mid level developer to know - how prototypical inheritance works and how “this” works in JS.


The two most popular statically typed OOP languages for servers are still C# and Java. Besides JavaScript, the most popular scripting language is Python (let’s ignore PHP for now) which is class based.

In the mobile space, Swift, Java, and now Kotlin are all class based.

“The zeitgeist” is different than “what companies are paying real money for”.

Besides, even with OOP based inheritance, the language is still “lying” just as much. Most developers who don’t understand how the lowest level of computers work don’t understand that when you call a function it’s always basically passing in a this pointer to one static instance of the function.


Object orientation in the sense of having some sort of data structure having some officially associated "methods" with it, and then building other structure on top of that, is alive, well, and not going anywhere any time soon despite the occasional functional programmer epiphanies.

Object orientation as in "There must be classes, there must be inheritance, each method and instance value must be able to be private, public, or protected, and have official constructors and destructors" and probably a couple of other things I'm missing, is also alive and well and not going anywhere any time soon, but is no longer the only definition of Object Orientation.

If you were not around at the time, you'll have to take my word for this, although the echos still reverberate in Javascript tutorials to this day (because of the way they talk about prototype-based inheritance as if you've got to be placated long enough about the fact that it isn't "true" OO long enough to learn what it actually is), but when it was released, Javascript (prior to any sort of "class" keyword) was not considered an "object oriented" language, because it didn't have "real" inheritance, or private values, etc. etc.

The point is that in a world where the definition of "OO" has expanded enough to encompass the original JS, it's a bit odd to bodge on a particular definition of OO so late in the process.

(People also argued back then about whether Python was object oriented (!), because it didn't have "true" support for private instances or methods. By contrast, I find it amusing when sometimes the question comes up "Is Go really Object Oriented if it has no inheritance?" and the answer generally amounts to "Honestly, who cares?")


Modern PHP is class based, the on going ignorance of PHP continues to insulate their own bubble


JS classes look like classes, behave like classes, and for all practical purposes are classes. They were one of the most useful additions to JS in recent years and make code so much more readable compared to hacking things together with the prototype system. With the added bonus, that you can still modify the prototype of a class to replace functions for all already generated instances of that class.


Don't disagree, but for React users onClick={this.doStuff.bind(this)} is pretty common for newbies who just read it somewhere and don't know of the perf implications of bind.

But personally I prefer prototypal inheritance and the everything is an object mentality.


> without the language actively lying to them

What does class mean?


In what way does it lie?


There are no classes, just functions. 'class {}' is a function. Try it.


I’m going to butcher this. But, in the grand scheme of things a “class” in most languages is just a group of related static functions. When you create an object from a class definition you are creating an object that holds values. When you call a method on that object, you are passing a pointer to the object to a static function.

It’s all just syntactic sugar in any language. Before the class keyword, it was just more explicit in JS.

A class is a function in JS, a class is a separate type in most other languages. I don’t see a meaningful difference.


> a class in most languages is just a group of related static functions.

In class oriented languages like C# and Java, sure. JS with ES modules has the alternative of using functions exported directly from a module.

The latter approach has the benefit of better tree shaking than the class based equivalent.


Would your definition classify most of lisp as a lie? It being implemented with a function shouldn't matter. What about the actual behavior is wrong for a class?


It's not implemented with a function. It literally is a function (object constructor).

class a {}

typeof a == 'function'

Objects in JavaScript are not class based. You can read here about the diferences:

http://www.ecma-international.org/ecma-262/6.0/#sec-objects


There is no 'class' type. That doesn't make classes fake, any more than a linked list or B-tree is 'fake' in C.

As that link says, objects are not fundamentally class-based because objects don't have to have a class. But you could add classless objects to almost any language without making their classes fake. And when it talks about inheritance, well, you can inherit state in C++ too, it's called 'static'.

Or in shorter form: It's not class-based but it does have classes.


There are no integers in Lambda calculus, only natural numbers Church numerals built out of functions. That doesn't make numeric support a "lie" in Lambda Calculus.


I'm not saying classes are fake. There are no classes in the JS runtime. It's all just objects and some property sharing via a prototype chain.

You can implement anything in any general purpose language. But that doesn't mean the language has that concept.


The language has the keyword and the implementation.

It's pretty normal for runtimes to not know about certain language concepts. The C runtime has never heard of switches or while loops, no big deal. If it's in the compiler it's part of the language.


Objects don't have a class in JavaScript. It's that simple. You'll not change that with word games.


You're missing the forest for the trees. A fixed and reusable prototype, with a constructor, is a class. And when that's part of the language spec, that means the language has classes.

And it doesn't matter what typeof says. I could take a C++ program, replace every instance of the word 'class' with 'struct', and it would work fine. It would still be using classes, even though typeof says 'struct'.

Typeof in javascript calls the constructor a function and it calls objects objects. That's correct and fine. You can't put a class itself into a variable in C++ anyway.

And if you really want to get into the gritty details, it's possible to make a very similar argument that C++ of all languages doesn't have classes. Sure they exist in the source code, but all that pretty syntax gets thrown away and you end up with a naked object or one that just has a pointer to a struct it inherits from. And there's not even a 1:1 relationship between source-code 'classes' and those structs!


> You're missing the forest for the trees. A fixed and reusable prototype, with a constructor, is a class. And when that's part of the language spec, that means the language has classes.

Yes, but prototype is not fixed. Prototype chain is not fixed (you can extend it at any point). All of that is changeable at runtime. You can also change the object's prototype at runtime.

It's all dynamic, and prototype ("class" as you say) doesn't provide any structure/constraints to the object. Thus it's pretty pointless to call it a class. It's just property sharing via a prototype chain. It's nothing like classes in languages like C++, Java, PHP, etc. And it doesn't help anyone understand the language, if you try to see it through that paradigm.


We are now in an intermediate hell of Babel and webpack though...the language is cool, but the toolchains are really crazy right now. Someone could dismiss my argument if they got a comfy setup but often times if you're doing even just a little trailblazing you get into some wtf territory. I spent all day this week setting up a karma test cause my node module was failing only in the browser so then I added Babel and karma-webpack, but then regenerator runtime kept giving me errors, then mixing require() and es6 import gave me insanity errors and I almost gave up. Deep googling gave me barely enough clues to get through it...


I’ve avoided any serious JavaScript development for years because of crap like this except when absolutely necessary. I’ve learned it well enough to get pass “full stack developer” interviews, but as soon as I get hired, I get migrated to the back end.

I’m just now putting the finishing touches on my first Node/Express microservice for production. I’m putting off front end development for another year or so.


I agree that the class keyword is far more readable. I never find myself using them in the "classical" sense, though I could see how it would be tempting for someone coming from, say, Java.


You could put callback back in the bag https://github.com/staltz/callbag-basics


It reminds me a lot of what happened with C++ in the past few years, and in contrast to C which has basically stayed still meanwhile (I know there's C99 and C11, but a lot of code out there is still C89 --- and IMHO that's the way it should be.)

I am very much against constant change in a fundamental platform. I should be able to write JS that works for decades without worrying about deprecation or any other sort of useless "churn". The less the platform changes, the more you can focus on understanding it and doing more. But of course it seems the majority of developers would rather have "new and shiny" over "old and stable", which I think is quite unfortunate.

The old saying is "do more with less". What's happening now in development, especially Web, seems to be more like "do less with more".


JS was born a horrible language. It needs to be fixed before it becomes so entrenched that you might write code in it that has to work for decades.


I've never thought of ES5 as simple, consistent, and easy to read. There are plenty of features that many devs wouldn't recognize (ie, "the bad parts"). They've fallen out of vogue because there are better ways to do things, but they're still part of the language. I, too, would love a simplification of the language, but as you mention, backwards compat is necessary.

And what does `case` not do that you want it to? Fall-through? Re: forced destructuring, if you want to switch on a value, why not just wrap it in an object, eg, `case({ someVal })`? It's not perfect, but I'd prefer consistently using `case` to the confusion you call out re: `case` vs `switch`.


The point is that case does not currently exist, but switch does, and is fine. If we could get rid of switch and replace it with this case proposal, that might be nice, but we can’t. Having the language with both existing side by side is, to me, worse than just living with switch.


> Since JavaScript has an especially high burden for backward compatibility you can’t really remove old cruft

You can't remove features from JS runtimes (browsers or node), but you can certainly remove them from a new language version. "use strict" already did it once in JS. It can be replaced/upgraded with "use ecmascriptX.Y" for each language version.

And transpilers targeting JS and tools like packers and minifiers can enforce language versions without declaring them in each file.


"use strict" was also highly contentious for a time. "use versionNumber" would have similar issues to the battles the web fought over DOCTYPES and "quirks" modes and browser compatibility.


ES5 took me months to understand well. And today, after years (and now on ES6/TypeScript) I cannot always properly describe some features of ES5 without seriously reviewing them.


there was nothing easy about var hoisting. modern javascript with a good linter is a lot cleaner and easier to read.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: