"Let" feels utterly pointless and worse, introduces unnecessary memory thrashing. If you "need" that fine a granularity of scope you are doing something horrifyingly wrong in your codebase such as re-using variable names in a confusing manner. Worse, because they don't lift you increase stack allocations and de-allocations since lifting was added to the language to address the fact it's not forward declarative. (Something C gets away with because it's designed for multi-pass compilation so all those inner scopes can be allocated at function entry)
When I said I wanted constants, "const" is not what I was asking for. I guess I should have said DEFINE. Though it would be nice if we could have unmutable object properties with something a bit less convoluted than defineProperty. See, the problem for me is that they are scoped, the one place I don't need constants! (unless it were a property on an object's "this") Then that whole "Symbol" thing almost sounds like what I want, except it's so poorly documented and explained you'd almost think it was a W3C specification.
The old joke, W3C specifications read like they were written in Finnish, translated to Japanese by a Russian, then Google translated to Engrish moist goodry me love you long time. There's a reason we all pretty much go straight to third party tutorials instead of the specification to learn the... uhm... "specification". I swear there are sections of the ECMAScript, CSS, and HTML specifications that you need to hire a gaggle of lawyers to explain.
Arrow functions are another thing that just takes the already agonizingly painfully cryptic BS the language inherited from C (I'm a Wirth guy) and makes it all the more complex and difficult to follow. And for what, one minor distinction in how "this" is handled and "wah wah, eyes bee two layzee too tiep teh wurd function"?!?
Though what's really annoying about arrow functions is how everyone is throwing them into code for nothing, often where callbacks and some newer methods -- like foreach -- are less efficient -- often painfully so -- than a simple old-fashined for loop.
But then when people don't even seem to realize that ANY expression can go in the middle of "for" what can you expect. There's a lot of new stuff people are just using without thinking about "is this an improvement over the old way or not" -- and the answer is quite often not!
"For" example rimshot I keep seeing stuff like this:
let allSpans = document.querySelectorAll('span');
allSpans.foreach((span) => {
// process span here
});
Thing is if you're using an immediate function on foreach, why are you even using a function? Much less, see the flub using querySelectorAll that has to go through every selector instead of getElemetnsByTagName which only searches tagName typically from a pre-built lookup table?
This:
for (var span of document.getElementsByTagName('span')) {
// process span here
}
.. and of course the pointer for 'span' would be allocated on function entry, which is why I wrap all my scripts in IIFE meaning scope bleed is a non-issue anyways. But if it really bugs you, fine, use let there. Don't even need the extra variable.
... and because we have removed the overhead of the function callback, it executes many, many, MANY times faster.
But then it often seems so many people are unaware of the highly powerful for/of. Not surprising when most people operating on nodeLists continued to brute force things in their legacy support instead of just:
var allSpans = document.querySelectorAll('span');
for (var i = 0, span = allSpans[i]; i++) {
// process span here
}
Basically if you need for/of in browsers that don't support it on a nodeList or other array iterable where you know none of your values are loose false, that's the fastest way to go through them. It's funny because that technique seemed to be gaining traction back in '03, but by '08 it had all but disappeared -- never could figure out why.
Even the examples given in various tutorials are -- IMHO garbage. For example I've seen this dozens of places:
this.nums.forEach((v) => {
if (v % 5 === 0) this.fives.push(v)
});
Why would you EVER do that -- especially if you like 'let' -- instead of:
for (let v of this.nums) {
if (v % 5 === 0) this.fives.push(v);
}
I don't get it. MAYBE for event handlers, but is it really THAT hard to do "function(e) {" instead of "(e) => {"... I know which one I'd rather look at if I'm trying to write comprehensible code, and it's not the flipping arrow functions!
Generally though this whole let, const, arrow function, and endless callback methods thing is introducing excess overhead and reducing code clarity, two things that always raise the hackles on my hackles.
That said, things like classList were riduculously overdue, shame that because browser implementations seem to work off the Array instead of the className string methods like classList.add, classList.remove and so forth end up slower than any decent regex polyfill for them. Which is sad when a regex or even multiple regex brute-forced by your own functions are faster than the native methods, but that's JavaScript arrays for you! Probably because they aren't actually Arrays, they're pointered lists!
That's not so much a problem with the specification though as it is the implementation. Honestly I don't think the 'array' part of classList should have even been a 'thing'.
I do think that proper Class declarations were a good idea, but to be frank the implementation feels as ugly and shoe-horned in as C++'s nasty slopped in any-old-way object implementation. (again, my prejudice is showing. I learned objects in smalltalk, object pascal, modula, and ADA) To the point that honestly, declaring conventional object as the constructor and using object.prototype as a object literal actually feels like a more reasonable approach -- even if extending the object in a new instance can be a bit of a wonk since you have to spin your own clone method.
A lot of things strike me as just encouraging bad practices; see "block scoped functions" where if you want another function, give it a new name. I've already hit up against namespace hell from people reusing variable and function names all over the place inside the same parent function. It's a confusing inconsistent mess that after some four decades of writing code, I really question what greenhorn came up with the idea.
"template strings" is another of the C mentality garbage I never wanted to see make its way over to JavaScript. I have always found C's printf to be confusing hard to follow garbage; I want to see that string closed, the variable there, and that string opened again for code clarity, not some goofy hard to follow handlebars type bull that for me often triggers false positives. This is only made worse by the allowance of operations inside it, which strikes me as the same type of foolhardiness as using "eval"!
It's all part of why I've come to the conclusion that when people say "syntactic sugar" what they mean is "cryptic to the point of intentionally trying to make programming harder than it has to be".
All that said, I love all the destructuring on assignment stuff, computed properties, the WAY past due proper unicode support, the new parameter handling that's way less of a headache than the "arguments" array, typed arrays (or as I call them, actual huffing arrays!)...
... and a lot of it I'm just indifferent to as it's not anything I would use when writing JavaScript; such as the moving of certain functions out of the global space and onto their associated objects. All this has done (for me) is result in having to type String(myVar) or Number(myVar) around a bunch of things to use the old method, resulting in zero real-world improvement. Oh noes, library functions in the global space!!! AAAAAAAAH!!! I swear it had to be some die-hard Java fanboy who thought JavaScript needed that.
Which makes sense given the mass exodus we've seen from Java to node.js by "people who follow the hot and trendy" if you think about it.
I can see that for consistency sake, but it's not really anything I think they should have wasted time on.
... and there are things that I understand, but would prefer to just avoid. Promises for example to me are a pointless addition for a language that's pretty much inherently single-threaded. It's at best a crutch for people who can't handle async coding, at worst a way to make a convoluted mess out of the simplest of things.
Shame for 99%+ of what I'm writing these days I can't even deploy a single bit of ES6, since I mostly work client-side where you can't even guarantee it exists yet. (though I have a side project I'm building in Electron that will definitely leverage a lot of it!) I'm sure if I did more with node.js other than ripping out large chunks of code and garbage frameworks that are screwing over clients, taking hundreds of K of code and reducing it to dozens, I'd have a better understanding of why some of this exists.
Although arrow functions do have a nice side-effect in that regard. Because IE has no clue what to make of them, their mere presence will make your entire script fail to even execute. I actually used that instead of browser sniffing or capabilities detection on a recent project so that the site could gracefully fall back to the non-scripted functionality, ignoring the attempt at scripting enhancement altogether.
But then client-side I write all my pages so that the page's base functionality and the most important thing a website can do -- delivering content -- works before I even think about adding JavaScript to the page. My scripting is there to enhance the user experience, not be the only means of providing it. As my buddy Dan used to say:
If you can't make a fully functioning useful page without client-side JavaScript first, you likely have zero business adding scripting to it!
Shreyansh Pandey
node, coffee and everything in between
Basis of Hatred
Any point or "feature" I mention here has made this list (or rather, got the Golden Raspberry Award) because of either of these things:
The last point might boil the blood of a lot of people, but I have seen some features of JavaScript being used incorrectly; to an extent that sometimes I think using ES5 would've fine here.
constReason: False Impression
Many new programmers, when starting with ES6 or above, think of
constas being immutable. It might be true for base types (boolean,string, and the likes) but it's not so true for objects (objects AND arrays).For example, let's say I have this piece of code:
const testValue1 = 'Hello, Hashnode!'; // Will not execute. Thank God. testValue1 = 'I love Java!';Perfect! It's doing what it's supposed to. However, let's look at ECMAScript 2019 Specification. In particular:
letandconstare actually built forlexical environment scoping(unlikevarwhere the scope leaked). They links are actually from the 2019 spec. Let's look at the 2015 spec where they were introduced so § 13.2.14] which says:If IsConstantDeclaration of d is true, then Let status be env.CreateImmutableBinding(dn, true). Else, Let status be env.CreateMutableBinding(dn, false).Alright. We are getting somewhere. Let's dig in deeper: an excerpt from
CreateImmutableBinding.Aahhhh! So it means that the variable reference is immutable and not the derivative thereof.
As we saw in the spec (13.2.14, ECMAScript 262), only reassigning references isn't allowed; that is to say you can't point it another value. However, whatever value you have can be mutated as long as it doesn't change the lexical environment.
Tagged Templates
Reason: readability
Almost everyone who has used
styles-componentsknows what this is:const Button = styled.a` display: inline-block; `It's a styled button! But for new readers, this is just probably a syntax error? Where is the parenthesis since
styled.ais supposed to be a function? What is this?These are tagged templates. You seem, when ES5 introduced templates, they also thought of introducing a function which works on them in case someone wanted to do some customized interpolation. I have used this as a part of a project I worked on where we created a
contenteditablewrapper around Draft.js and used a pluggable approach to the exporters, and the core logic of the adapters relied on tagged templates.For those of you who don't know what these are, let's take a quick example:
const wrapInHead = ( str, ...vars ) => { // str is an array which contains strings split by where there was an interpolation. // and vars is the respective variables which were to be filled. let finalString = ''; for ( let i in str ) { // we use short circuiting since the last value is str will always be "" and if the parts are greater than // the value, it'll just give a blank. finalString += str[ i ] + ( vars[ i ] || '' ); } return finalString; };Very cool!
But think about it. Do we seriously need it? I mean, yeah, it is certainly good and useful but if you're a new dev, will you immediately know what it is? And most of the good JS courses don't touch on this either.
Because of this reason, I really don't like tagged template literals. They are good, but only when the entire team know what they are or if you are working on something personal.
A big bummer for new devs! And really confusing too.
() => {}Reason: gives a false impression/improper usage
This is a big one!
With the introduction of arrow functions, our code bases became a lot cleaner!
function testFunction( testVar, function( cbVal ) { const args = Array.from( arguments ); // Do something... } );With arrow functions, these became:
const testFunction = ( testVar, cbVal => { const args = Array.from( arguments ); // Do something! } );After this modification, we broke an entire build. Why? Because people thought
() => {}is the same afunction name() {}.NO. It is NOT. We hear the term lexical scoping a lot. What that means is that the scope of the current function or block is based on its parents or ancestors (sometimes). However, arrow functions do not have their own
thisorarguments. And because of this, they use whatever the callee provides. Many, including me, thought that it was the same and we messed up.function testFunction( test ) { console.log( arguments.caller ); } const newFunction = () => { testFunction( 'Hello World' ); };This should print
newFunction()right? Because that's the one callingtestFunction()? Right? NO! Arrow functions are treated as anonymous functions and hence, they don't have a function signature in call stack. (Try that code in the dev tools console.)And this also makes them really bad for any code which has logging since the function signature will never resolve in the logs.
argumentsReason: JUST BECAUSE/readability/improper usage
The
argumentsspecial variable contains the list of arguments passed to a specific function. This is especially useful when you don't know the number of arguments you'll receive; here, you useargumentswhich behaves like an array.What do you mean: behaves?
Right. When you execute this:
console.log( Object.prototype.toString.call( arguments ) );You get
[object Arguments]. It is so special that it has a separate type. Wow.But then you can access the values like
arguments[ 0 ], etc. And you can callconst args = Array.from( arguments );Weird. This object also have some (now deprecated) properties:
caller- the name of the function which called the current functioncallee- the current function.Note: this is not a ES6 feature as such but it is something which wasn't patched in JavaScript.
Disclaimer
I know that these "features" are used very extensively, even by me, but I am sure TC39 could've done a better naming jobs. But well, programmers are bad at naming things anyway! xD
This list isn't exclusively ES6. I have tried to include some weird parts of ES5 and 4 as well.