Presumably people will only use it in situations where null (or undefined, or however it works) is a valid and expected value for a particular property.
In TypeScript, these properties would explicitly have a `MyType|null` or `MyType|undefined` type.
TC39 (who defines Javascript) is a collaboration of several companies, so I'm not sure who Apple could/would even sue if this was proposed and adopted for JS. I doubt we would see legal activity over it.
Groovy had the same feature well over a decade ago[1] (at least in 2005) and other languages possibly even before that. Swift was first announced in 2014.
Swift's is also tied to their `Optional<T>` wrapper type, any potential JS implementation would be more like Groovy's.
why no one proposes as feature a header to make javascript strict with the newest standard and deprecate and drop compatibility from previous versions in the newer ones?
Or even just complete support for asmjs with interop and program in completely other languages, especially since the person who wrote this seems to want to program in languages with different semantics...
My point is, why continue bloating javascript if its main weakness are some of its historical design issues (preserved only for backwards compatibility.)
Yes, this is a big issue for JS. So much effort is spent trying to make JavaScript be some other language instead, by developers who are stuck building stuff in JavaScript because they want to run it in the browser.
At this point programming in other languages makes more sense and is arguably a better vision for “the future of JavaScript.”
Actually it is a pity that so many languages compile to JS but have an FFI which is not really friendly towards the programmer or that requires some code to write adaptors etc. Let's see how it evolves with asmjs.
I do not think discipline is a reliable factor in many aspects... once you try a good compiler or a well designed/consistent API there is no way back. Look for example at C++, no one knows all of it and the newer a developer is the more they want to use all its features, and then there are the templates... which do not add much of clairity or sanity.
honestly, I would see dropping features and select a leaner ES6+ version within the html tag as a very good "feature". The strict mode also does not fix some historical inconsistencies and design bugs just preserved for compatibility, for example methods on default arrays vs methods on the querySelector results.
There will probably always be a snarky, jejune dismissal in every JavaScript thread, but commenters should think to themselves, “It doesn't have to be me.”
I had that thought, but I don’t think it’s that unreasonable to hold JavaScript as a lost cause. Enough large projects are just using JavaScript as a sort of bytecode for the web anyway.
I also find personally that recognising something as bad is the first step to making something better.
That's fine, but we're here to gratify our intellectual curiosity. There's nothing wrong with recognizing as such, it just needs to be done thoughtfully and informatively so we can have a discussion about it.
An interesting thought for the future is if wasm will make other languages viable on the web. It would be interesting to see if anything else picks up steam as a serious competitor to JavaScript.
The advantage of straight JS is that you don't have to compile it. Once you accept a compilation step you have quite a few pretty good alternatives like TypeScript.
Given that there's a WASM backend to LLVM, once WASM can be used without the need to interface with JS then any language with a WASM frontend (there'll probably be the need for some basic library support) will become a viable replacement.
At the moment however (I believe that it's still the case), WASM still needs to interact with the DOM via JS, so there's a minimum amount of JS boilerplate required for bootstrapping.
Is it just me or are those really making Javascript weirder?
const func = do {
let result;
() => {
if (result === undefined) {
result = someComputation();
}
return result;
};
};
Shouldn't that be "if (result === undefined) then someComputation else result"? Why import functional style and proceed to make it inherently imperative with a return in the middle of the block?
obj?.foo?.bar?.baz
The point of optional chaining is that you can place functions within the chain. That seems to be a very surprising and useless way to do them.
It's lazy loading - someComputation() is only called if func() is called, and called only once. Perhaps you might prefer: result || (result = someComputation())
const func = (() => {
let result; // cache
return () => {
if (result === undefined) {
result = someComputation();
}
return result;
}
})();
You could write it without the immediately invoked arrow funciton or a do expression if `result` is in the same scope as `func`
let result;
const func = () => {
if (result === undefined) {
result = someComputation();
}
return result;
}
Result is in the same scope as func, inviting anyone to read from or write to it.
In all three examples `func` is assigned a zero argument function. That function returns the result of calling `someComputation`, but it stores the value in `result`. If `result` is not undefined `func` returns `result`. `func` is a cached, memoized, or lazy-loaded version of someComputation.
A do expression will return the last evaluated expression in it's scope, in this example that expression is `() => {...}`, not `return` because the inner anonymous function wasn't called.
---
I think a clearer demonstration of do expressions is an assigning switch or ifelse
const value = do {
switch (someString) {
case 'Monday':
0;
break;
case 'Tuesday':
1;
break;
...
}
}
const other = do {
if (isPrime(n)) {
'prime'
} else if (isOdd(n)) {
'odd'
} else {
'even'
}
}
The switch example seems odd to me, since in that case getting rid of the `do` expression and using returns turns out to be shorter than futzing around with breaks.
const value = (() => {
switch (someString) {
case 'Monday':
return 0;
case 'Tuesday':
return 1;
...
}
})();
Oh, your examples are better. Except for the break on the functional switch (that is weird again), they are how it should be done.
Is the example on the article wrong? A functional "if" shouldn't accept just one side of the branch, a functional switch shouldn't accept a "break", and although it's not absurd that a functional code block accepts a "return", it is not usual.
I agree, these are contrived examples, but I know that I have reached for inline logic many times in my career. To me another code smell is a scope with too many variables and do expressions are a tool to reduce the scope of variables. Refactoring to named functions will introduce another named variables.
Javascript doesn't have private modules so moving logic to helper functions in util modules doesn't reduce scope bloat.
function memoize = function (fn) {
var value
return function() {
return value !== undefined ? value : (value = fn())
}
}
var func = memoize(someComputation)
Why would anyone want to write that do/iife abomination inline?
I love these proposals and while it probably makes sense for me to learn more about the languages that already implement it there's no denying that having them in JS is a win. =)
What we really need isn't more syntax, it's core features like threads, operator overloading, a large std library, some real form of serialization (JSON doesn't come close to counting), etc.
How is operation overloading a core feature and not more syntax?
I like the infix proposal better. Operation overloading has a larger degree of obfuscation.
Or even more basic things like "a date/time implementation that isn't a horror show" or "\w that behaves like \w in every single other regex implementation out there when the unicode flag is on".
I was tempted to cite Antoine de Saint-Exupéry's quote on perfection, but we all know that, right?
But then again, even the author mentions at the beginning the "risk of adding too much" and, well, I wonder if this has not already happened to JavaScript.
JS is a language where new features can only be added by changing the language itself. There are other languages where new features can be implemented as third-party libraries. This allows people to experiment with new approaches to problems, try out new features and test them in real world. Once the community agrees on their usefulness, they are integrated into the standard library so everyone can profit. These are languages where you can't really remove that much, from the language itself. And any new language features (changes to the core language itself) are driven by "what is the minimum change we need to do to allow third-party code to grow even better" rather than … well, whatever that thing is that the JavaScript community is doing.
1.1 Comparing objects by value
I _think_ I'd prefer a standard `equals` method, or more likely a `[Symbol.equals]` method. With `[Symbol.equals]` and `[Symbol.hash]` we could also build collections for objects.
I feel like there's something I'm missing with do expressions. Are they purely syntactic sugar? What can I do with a do expression that I can't do with an immediately invoked anonymous function?
The examples the article gives don't really look all that syntactically different to me.
const func = (() => {
let result; // cache
return () => {
if (result === undefined) {
result = someComputation();
}
return result;
}
})();
const func = do {
let result;
() => {
if (result === undefined) {
result = someComputation();
}
return result;
};
};
And I guess I wouldn't be able to recurse in a do expression like I can with a named function? So it's not like I can actually universally stop using immediately invoked expressions everywhere.
Syntactic sugar I not a bad thing. In any sufficiently complex language, most features can be simulated with other primitives. If we didn't want that, we'd all be writing lisp.
In this specific case, the IIF is harder to read. You have to go all the way to the end of the expression in order to realize intent. With 'do', you see it right away.
Like anything in engineering, it's a question of trade-offs. Do the benefits of better readability outweigh the downside of making the language more complex?
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.
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?")
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?
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.
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.
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.
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 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.
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.
> 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 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.
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
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.
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.
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 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.
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.
> 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.
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".
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.
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's some interesting stuff in here, but I'm noticing a large chunk of it is syntactic sugar.
Keeping the extensible web manifesto in mind, I wonder if maybe what we really want is to have some kind of standardization around transpiling code.
With new features like promises, we had extensive testing via user libraries before they made their way into the language. But with these kinds of syntactic features, that doesn't seem to be happening.
If a feature is almost purely syntactic sugar, maybe it would make more sense for it to be supplied via a transpiler rather than being baked into the core language? Are there pain points or disadvantages to libraries like Babel that we could address via the core language instead?
Yes it makes the toolchain more complicated and it makes debugging slightly harder. On top of that having it as a standard means you can go anywhere and just use it without worrying about which transpiler they are using.
> Yes it makes the toolchain more complicated and it makes debugging slightly harder.
Are there ways we can help with this? Improvements we could be making to sourcemaps? Maybe we should be looking into adding new descriptors that are designed specifically to help generated code get read by debuggers? Maybe some kind of support for marking regions of code so you can run multiple transpilers on the same codebase?
I dislike transpilation in its current form, but I can think of things off the top of my head that would make it easier.
> On top of that having as a standard means you can go anywhere and just use it without worrying about which transpiler they are using.
Fair enough, there are definitely advantages to having a large set of core features. But if you look at languages like modern C++, it seems that there are also disadvantages that come along with that.
Given that the web is far more permanent than most other languages (we can never remove a JS feature), are the advantages of having one code style worth the disadvantages of not being able to test new syntax in the wild before we standardize?
There's a balance here -- I like lodash, a lot. But I don't want lodash to be part of the core language. Maybe some syntax fits into that category as well?
But why not have a larger standard library for Node for instance so you don’t have so many dependencies and have separate optional libraries for the browser?
Note that Node is actually in a much better position than Javascript as a whole, because Node apps can be shipped with a specific version of Node. This means that the Node team actually has more power than web browsers to deprecate old features and remove parts of the library, as long as they do so carefully. It is much harder to make breaking changes in a browser than it is to make them in Node.
One middle ground I've seen people suggest is to have officially "blessed" external libraries that still need to be imported, but that are cached locally or have special privileges. For example, JQuery was at one point so common that the Firefox dev tools actually ship with a subset of the JQuery API enabled -- just so devs that are used to working with it can use it in the debugger on any site.
I look at JQuery as a massive success story for how 3rd-party extensions should work. It was extremely ubiquitous, it did influence the core language, but now we've grown and because it was kept separate, we're still free to abandon it and move on.
On the Node side maybe that means that Node ships with dependencies that still need to be added to your package.json, but that are always bundled in your installation so that you have them locally and don't need to hit a network request when you type `npm install X`.
Also on the Node side we have modules that are built by the core team, but just not distributed in the core library. This helps out a bit with security, because you can still have a large organization like Google maintaining a feature and validating it, they just ship it separately from their browser.
>Are there pain points or disadvantages to libraries like Babel that we could address via the core language instead?
I think the main disadvantage is that compilers need to target something, and then JS engines need to be able to optimize that code even after it's transpiled. That's something that bringing these features into the language itself can help.
Take a look at async/await for an example of this. For a fairly long time it was extremely slow to run async/await when it was compiled to es5, because the subtleties required some fancy work on the part of the compilers, even going so far as to include a small runtime to achieve it.
Once it was supported by the JS engines themselves, it pretty quickly matched the speed of code full of `.then` with promises.
It's obviously not impossible, but I'm guessing it's much harder. There's a reason why the x86 architecture is still getting new features and additions, and it's not just for the comfort of people writing raw assembly.
Then you need to think about other tooling. Linters, compilers, static analyzers, minifiers, they all need a way to analyze the code you are writing. If that code is using features that aren't "standard" (or are on track to become standard maybe), then it would quickly get much messier than it already is.
You aren't just talking about compiling now, but also a unified way of adding "plugins" to linters, compilers, static analysis tools, syntax highlighters, minifiers, and more. Because adding a "syntax plugin" is a non-starter if it means we can't minify our code or run our linter, and creating a new "syntax plugin" would be much harder if you also had to make plugins and changes to all of those things to even let it begin to become useful.
Adding this stuff to the language is where it belongs, things like types on the other hand I firmly believe belong in "compile to JS" languages or dialects like TypeScript or Flow. That's a great abstraction level for that, simply because it doesn't change the output of the code (for the most part), it just enforces restrictions on it.
You could use the same technique as asm.js for acquiring and handling integers. Unary `+` always produces a Number or an exception. Bitwise operators treat their operands as a sequence of 32 bits, so a bitwise OR with 0 will cast the value to a 32-bit integer:
+value|0
There's also TypedArray [0]. I think you'll be interested in the BigInt proposal [1] [2], which is stage 3. It's already available in Chrome, and behind a flag in Firefox. Now that it's possible to accurately represent 64-bit values, we're getting BigInt64Array and BigUint64Array.
> I want actual proper integers. Not doubles, just integers.
Why exactly do you want that? Is it arbitrary precision you want? Is the 56 bits or whatever not enough for your use-case? Or do you find the range checks are too expensive in practice?
I have a feeling I'm walking into a very dumb question/obvious answer, but here goes.
If you're putting getFoo at whatever level of scope that you'd run it over multiple bar objects that don't have it bound, why is it conceptually different than writing it as
getFooOfBar(this_bar:hasfoo) => this_bar.foo
(If you get my drift from the terrible notation; and you could drop the type-safety and just take an any obj if we're being more literal to your pseudocode)
I had honestly not seen the bind operator used in at least the last decade, so perhaps the dissonance is just whatever corner of tech I work in having a distinct style/idioms.
I'd like to see a real, useful implementation of weak references, for use in value caches, or a way to manually take over garbage collection for some objects.
Basically if I want to keep a cache of expensive objects that will grow monotonically, I need to do a lot of work to make sure I'm not leaking memory right now, but if I could instead store in a Map<string, weakref>, it'd be much easier, things could go out of ref and I could deal with that.
Yeah weakmap and weakset basically do the opposite of what I want. The keys are weakly held rather than the values, which does no good for an ORM cache. One cannot even iterate over the keyset in a weakmap (so you can't do a lousy o(n) lookup version of what I want.
Absolutely this. I've done work in both Swift and JS (well, Typescript) and every feature on my wishlist is from Swift. Null operator like you suggest, guard statements and structs, which are the best equivalent to value-type objects as outlined in the article.
Recently started using optional chaining in our codebase (via Babel 7) and it's great so far. Also gives us proper static typing safety unlike Lodash `get(...)`
They’re plenty useful if you want to write a library and distribute it (either publicly or within an org) that tells users of the library what they did wrong (other developers) without having to insert type checking boilerplate into every single method.
Types are a subset of contracts. Contracts are plenty useful at runtime, as evidenced by the fact that every language that offers design-by-contract (most of which are statically typed) still provides runtime checking facilities. Furthermore, they're usually enabled by default for preconditions, which is what argument types are.
Nobody will handle such errors - contract violations are not supposed to be handled, they're a coding bug. You "handle" them by fixing the caller such that it never violates the contract. The benefit of having a runtime check is that it can detect the error instead of silent wrong behavior potentially followed by state corruption and further bugs. Most of the time, these will fire in automated tests, anyway.
Types :) I tend to use underscore for just _.get or _.set these days as I have most everything else I want so having some built in way to do that would be nice but the elephant in the room here is the toolchains being built quietly behind the scenes that support web assembly from strongly typed languages with much better load times and performance. The WALT one and the oCaml React one is pretty cool but would be nice if there was an official strong-typed JavaScript version for this scenario being built as a spec.
I know it will never happen, but something like LINQ in C#. Not just functions that iterate over collections, but the whole idea of language integrated queries where code is turned into expression trees that can be parsed and translated by providers.
For instance in C# you can take the same LINQ expressions and they can be translated to sql by EF or MongoQuery by the Mongo driver at runtime.
You can implement three classes for that interface - one that uses EF, one that uses Mongo, and one that just uses in an memory List for unit testing and they all translate the expression to the underlying data store.
Theoretically, you could have an “expression” type in JS that can be parsed by the provider.
What you’re looking for is the backtick in lisp. Or more generally: lisp macro’s where a function can inspect the ast of an argument and base it’s result on that.
For those who gave never used linq: this allows the above function to translate the function to a sql, sparql or mongo query depending on the currently selected backend
I feel like you could take a subset of JavaScript and make a new "JSLite" language that forces you to do things in one, proper best way. It would be a great way for new developers to learn modern techniques.
A lot of great and well-thought-out suggestions here. Whenever taking deeper dives into Javascript, I tend to find myself on 2ality.com though google searches and whatnot. They do well when it comes to breaking down the language and features.
I'm a fan of 2.2: destructuring switch, although I occasionally employ something similar in JavaScript and other languages with a `switch` statement:
const resource = await fetch(jsonService);
switch (true) {
case resource.status === 200:
console.log(`size is ${resource.headers['Content-Length']}`);
break;
case resource.status === 404:
console.log('JSON not found');
break;
case resource.status >= 400:
throw new RequestError(res);
break;
}
It's missing the destructuring aspect, so the first case is a bit more verbose, and I imagine there's better potential to optimize something like the OP's example. But it has a similar effect to the example and works today.
Slightly related: I really wish `case` worked with brackets instead of break, similar to OP's example. It looks cleaner and is easier to visually parse. I suppose that's only better if you prefer brackets (which I do).
Needs more punctuation. I don’t know about the rest of y’all but I get disappointed when I look at JavaScript and it’s not awash in a sea of angle brackets.
But seriously, be careful not to ruin it with too much “I need this!”
287 comments
[ 2.9 ms ] story [ 287 ms ] threadWill it lead to many JS bugs simply disappearing and applications getting more stable or will it just obfuscate bugs in the long run?
In TypeScript, these properties would explicitly have a `MyType|null` or `MyType|undefined` type.
"or will it just obfuscate bugs in the long run?"
Probably. :shrug emojii:
But more stable in the sense of turning hard crashes into UI quirks.
https://github.com/apple/swift/blob/master/LICENSE.txt
TC39 (who defines Javascript) is a collaboration of several companies, so I'm not sure who Apple could/would even sue if this was proposed and adopted for JS. I doubt we would see legal activity over it.
[0]: https://github.com/jashkenas/coffeescript/blob/1.0.0/lib/par...
Swift's is also tied to their `Optional<T>` wrapper type, any potential JS implementation would be more like Groovy's.
[1] https://web.archive.org/web/20050815012153/http://groovy.cod... , bottom of the page
Which has been around since 1998 at least (When Haskell 1.3 added monads)
Or even just complete support for asmjs with interop and program in completely other languages, especially since the person who wrote this seems to want to program in languages with different semantics...
I wouldn't expect him to discuss "dropping compatibility" and "completely other languages"
At this point programming in other languages makes more sense and is arguably a better vision for “the future of JavaScript.”
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
I also find personally that recognising something as bad is the first step to making something better.
(I write javascript almost every day)
At the moment however (I believe that it's still the case), WASM still needs to interact with the DOM via JS, so there's a minimum amount of JS boilerplate required for bootstrapping.
In all three examples `func` is assigned a zero argument function. That function returns the result of calling `someComputation`, but it stores the value in `result`. If `result` is not undefined `func` returns `result`. `func` is a cached, memoized, or lazy-loaded version of someComputation.
A do expression will return the last evaluated expression in it's scope, in this example that expression is `() => {...}`, not `return` because the inner anonymous function wasn't called.
---
I think a clearer demonstration of do expressions is an assigning switch or ifelse
Is the example on the article wrong? A functional "if" shouldn't accept just one side of the branch, a functional switch shouldn't accept a "break", and although it's not absurd that a functional code block accepts a "return", it is not usual.
https://www.w3schools.com/js/js_switch.asp
Javascript doesn't have private modules so moving logic to helper functions in util modules doesn't reduce scope bloat.
I wish I knew about python's [itertools](https://docs.python.org/3/library/itertools.html) before I started making [streaming-iterables](https://www.npmjs.com/package/streaming-iterables#api) for example.
But then again, even the author mentions at the beginning the "risk of adding too much" and, well, I wonder if this has not already happened to JavaScript.
The examples the article gives don't really look all that syntactically different to me.
And I guess I wouldn't be able to recurse in a do expression like I can with a named function? So it's not like I can actually universally stop using immediately invoked expressions everywhere.In this specific case, the IIF is harder to read. You have to go all the way to the end of the expression in order to realize intent. With 'do', you see it right away.
Like anything in engineering, it's a question of trade-offs. Do the benefits of better readability outweigh the downside of making the language more complex?
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.
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.
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.
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 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?")
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
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.
You can implement anything in any general purpose language. But that doesn't mean the language has that concept.
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.
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!
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.
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.
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.
But personally I prefer prototypal inheritance and the everything is an object mentality.
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.
In particular, I still run into usage of `call` and `apply` on a regular basis.
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:
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?"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:
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."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 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 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).
What does class mean?
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.
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.
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".
That ship sailed long ago.
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`.
Keeping the extensible web manifesto in mind, I wonder if maybe what we really want is to have some kind of standardization around transpiling code.
With new features like promises, we had extensive testing via user libraries before they made their way into the language. But with these kinds of syntactic features, that doesn't seem to be happening.
If a feature is almost purely syntactic sugar, maybe it would make more sense for it to be supplied via a transpiler rather than being baked into the core language? Are there pain points or disadvantages to libraries like Babel that we could address via the core language instead?
Are there ways we can help with this? Improvements we could be making to sourcemaps? Maybe we should be looking into adding new descriptors that are designed specifically to help generated code get read by debuggers? Maybe some kind of support for marking regions of code so you can run multiple transpilers on the same codebase?
I dislike transpilation in its current form, but I can think of things off the top of my head that would make it easier.
> On top of that having as a standard means you can go anywhere and just use it without worrying about which transpiler they are using.
Fair enough, there are definitely advantages to having a large set of core features. But if you look at languages like modern C++, it seems that there are also disadvantages that come along with that.
Given that the web is far more permanent than most other languages (we can never remove a JS feature), are the advantages of having one code style worth the disadvantages of not being able to test new syntax in the wild before we standardize?
There's a balance here -- I like lodash, a lot. But I don't want lodash to be part of the core language. Maybe some syntax fits into that category as well?
Note that Node is actually in a much better position than Javascript as a whole, because Node apps can be shipped with a specific version of Node. This means that the Node team actually has more power than web browsers to deprecate old features and remove parts of the library, as long as they do so carefully. It is much harder to make breaking changes in a browser than it is to make them in Node.
One middle ground I've seen people suggest is to have officially "blessed" external libraries that still need to be imported, but that are cached locally or have special privileges. For example, JQuery was at one point so common that the Firefox dev tools actually ship with a subset of the JQuery API enabled -- just so devs that are used to working with it can use it in the debugger on any site.
I look at JQuery as a massive success story for how 3rd-party extensions should work. It was extremely ubiquitous, it did influence the core language, but now we've grown and because it was kept separate, we're still free to abandon it and move on.
On the Node side maybe that means that Node ships with dependencies that still need to be added to your package.json, but that are always bundled in your installation so that you have them locally and don't need to hit a network request when you type `npm install X`.
Also on the Node side we have modules that are built by the core team, but just not distributed in the core library. This helps out a bit with security, because you can still have a large organization like Google maintaining a feature and validating it, they just ship it separately from their browser.
I think the main disadvantage is that compilers need to target something, and then JS engines need to be able to optimize that code even after it's transpiled. That's something that bringing these features into the language itself can help.
Take a look at async/await for an example of this. For a fairly long time it was extremely slow to run async/await when it was compiled to es5, because the subtleties required some fancy work on the part of the compilers, even going so far as to include a small runtime to achieve it.
Once it was supported by the JS engines themselves, it pretty quickly matched the speed of code full of `.then` with promises.
It's obviously not impossible, but I'm guessing it's much harder. There's a reason why the x86 architecture is still getting new features and additions, and it's not just for the comfort of people writing raw assembly.
Then you need to think about other tooling. Linters, compilers, static analyzers, minifiers, they all need a way to analyze the code you are writing. If that code is using features that aren't "standard" (or are on track to become standard maybe), then it would quickly get much messier than it already is.
You aren't just talking about compiling now, but also a unified way of adding "plugins" to linters, compilers, static analysis tools, syntax highlighters, minifiers, and more. Because adding a "syntax plugin" is a non-starter if it means we can't minify our code or run our linter, and creating a new "syntax plugin" would be much harder if you also had to make plugins and changes to all of those things to even let it begin to become useful.
Adding this stuff to the language is where it belongs, things like types on the other hand I firmly believe belong in "compile to JS" languages or dialects like TypeScript or Flow. That's a great abstraction level for that, simply because it doesn't change the output of the code (for the most part), it just enforces restrictions on it.
Come on...the title is clickbait I had to. And no I didn’t read an article about modern javascript.
I also missed the awesome bind-operator. EG:
https://github.com/tc39/proposal-bind-operator[0] https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
[1] https://github.com/tc39/proposal-bigint
[2] http://2ality.com/2017/03/es-integer.html
Why exactly do you want that? Is it arbitrary precision you want? Is the 56 bits or whatever not enough for your use-case? Or do you find the range checks are too expensive in practice?
If you're putting getFoo at whatever level of scope that you'd run it over multiple bar objects that don't have it bound, why is it conceptually different than writing it as
(If you get my drift from the terrible notation; and you could drop the type-safety and just take an any obj if we're being more literal to your pseudocode)I had honestly not seen the bind operator used in at least the last decade, so perhaps the dissonance is just whatever corner of tech I work in having a distinct style/idioms.
You would need to use `function` for this:
Basically if I want to keep a cache of expensive objects that will grow monotonically, I need to do a lot of work to make sure I'm not leaking memory right now, but if I could instead store in a Map<string, weakref>, it'd be much easier, things could go out of ref and I could deal with that.
return data?.error?.message ?? "no error";
Also pattern matching would be a big win.
https://github.com/tc39/proposal-optional-chaining
https://github.com/tc39/proposal-nullish-coalescing
Recently started using optional chaining in our codebase (via Babel 7) and it's great so far. Also gives us proper static typing safety unlike Lodash `get(...)`
Either all variables shall have types and so static, compile time analysis can be performed.
Or no types at all.
Types are useful only if they can be checked/verified at compile time.
Types in only arguments cannot be verified by compiler. So type mismatch can be checked only at runtime. So would be this:
Is essentially this: And that rises the question: who and at what moment will handle such errors?Nobody will handle such errors - contract violations are not supposed to be handled, they're a coding bug. You "handle" them by fixing the caller such that it never violates the contract. The benefit of having a runtime check is that it can detect the error instead of silent wrong behavior potentially followed by state corruption and further bugs. Most of the time, these will fire in automated tests, anyway.
For instance in C# you can take the same LINQ expressions and they can be translated to sql by EF or MongoQuery by the Mongo driver at runtime.
var retiredMales = custRepo.Find(c => c.Age > 65 and c.Sex == “M”)
and then have an interface with a member:
List<Customer> Find(Expression<Func<Customer,bool>> expression):
You can implement three classes for that interface - one that uses EF, one that uses Mongo, and one that just uses in an memory List for unit testing and they all translate the expression to the underlying data store.
Theoretically, you could have an “expression” type in JS that can be parsed by the provider.
For those who gave never used linq: this allows the above function to translate the function to a sql, sparql or mongo query depending on the currently selected backend
I'm a fan of 2.2: destructuring switch, although I occasionally employ something similar in JavaScript and other languages with a `switch` statement:
It's missing the destructuring aspect, so the first case is a bit more verbose, and I imagine there's better potential to optimize something like the OP's example. But it has a similar effect to the example and works today.Slightly related: I really wish `case` worked with brackets instead of break, similar to OP's example. It looks cleaner and is easier to visually parse. I suppose that's only better if you prefer brackets (which I do).
But seriously, be careful not to ruin it with too much “I need this!”