Posted
What we call "javascript", the language behind every bit of fancy interactiveness on the web today, is really just a for-browsers implementation of ECMAScript. Just like the W3C's specifications (HTML5, CSS3, etc.), ECMAScript gets updated now and then. Very soon, we'll see version 6 finalized, but even before then, some of the features are trickling into the big browsers. (Well, the ones that update.) Here's a rundown of the good bits:
Let
Has anyone ever needed a variable just for a for loop? Or for a couple uses within an if or a switch, but there are too many other variables needed in the same scope to pass it all at a function? If so, then good news: the let keyword. The new let keyword works just like var, but only declares the variable within the containing block.
I haven't found any mention of whether variables declared by let will be promoted to the top of their block as with var and function scopes. For consistency's sake, I prefer promotion over strictly following source order.
The best part, hands down, is that let finally gives everyone the for loops they always wanted. Everyone started out writing
for (var i = 0; i < myArray.length; i++) {
// awesome code
}
only to learn later that the proper form is this ugly mess
var i, il;
// All your other code
for (i = 0, il = myArray.length; i < il; i++) {
// awesome code
}
// watch out, i and il are still in scope!
Now, here's what it looks like with let
for (let i = 0, il = myArray.length; i < il; i++) {
// awesome code
}
// no trace of i or il left in scope
For...of
ECMAScript has a largely ignored cousin to the standard for(;;) loop, the for...in loop. It uses a syntax like
for (let x in myCollection) {
alert("item " + x + " = " + myCollection[x]);
}
In for...in, x is the key or index (depending on the collection) of the collection, and the value is accessible only by manually pulling the value out, i.e. myCollection[x]. ECMAScript 6 will give an excellent complement to for..in: for...of. It works like this
for (let c of awesomeColors) {
alert(c + " is an awesome color.");
}
See what they did there? No more index; it's only values. Honestly, this seems too simple to just now be considered. But, like let, it's what everyone always wanted anyways.
Keeping hope alive
The sheer symmetry of for..in vs for...of makes me want for...of to get approved. Also, for all the each, walk, and map methods on enumerable objects, sometimes it just isn't readable to string everything into one giant functional-language expression.
Alternatively, scrap for...of entirely to empower for...in. I propose a true foreach, based on for...in:
for (let word: definition in awesomeWords) {
alert("The word " + word + " means: " + definition");
}
The : definition part makes it easily parser-distinguishable from a normal for...in, it uses no new keywords, and the key: value construct is identical to what's used in object literals.
Map and Set
I should probably start by mentioning my deep and concerning love of set theory. Yes, I'm that kind of person. That out of the way, the bread and butter of set theory is, eponymously, the set. A set is an unordered collection of objects with no duplicates. For example: color names are a set, as there's no intrinsic order (ignore rainbows for a moment) and having "taupe" in there twice is just nonsense.
A map is just a set where each element of the set has one and only one associated value. This allows for constructs like a dictionary: the words form a set, and the element associated with each word is itself a set, but of definitions.
ECMAScript 6 proposes native support for a Set and Map object. Set will support the methods add(element), delete(element), and has(element). Map will support the methods delete(key), get(key), has(key), and set(key, value).
Admittedly, this sounds like pretty primitive behavior, and it is. It really should've been there earlier. Regardless, even now, the most basic way to accomplish this is by abusing an array or object. Need a set? Just create an empty object and start throwing boolean true at some string index in square brackets. Want to iterate your set? Don't forget the hasOwnProperty() check; you wouldn't want to accidentally hit the prototype chain on the object. Want to map some objects directly to some other value? Too bad, you'll have to write your own container, maybe with some nice fragile parallel arrays. Remember to keep your keys sorted and binary search into them, we wouldn't want O(n) performance.
Scrap all that. Soon, we'll have native implementations with likely near-hash performance (if either performs worse than O(log2(n)), color me disappointed). Also, and this is staggeringly important: the elements (for Sets) and keys (for Maps) can be virtually anything, objects included. It's so picky, even +0 and -0 are different keys. It will soon be possible to directly map HTML elements or (maybe) functions to values.
Keeping hope alive
There are, however, still two glaring omissions. First, I demand statement-level iteration. Some sort of for construct would be just dandy (see above). Second, union, intersect, and difference. These are absolutely vital set theory operations, and they need to be in there.
Fat arrows
Anonymous functions are the greatest construct in programming. Functions as values is just a heartwarming concept. Functions returning functions is almost as awesome as functions being passed to functions. There are a few problems with this in the present version of ECMAScript: it's really verbose, functions are bulky constructs, and sometimes the this keyword stops making sense. Luckily, the "fat arrow" notation takes care of these concerns and more.
Here's how it works: zero or more arguments in parentheses, a fat arrow (=>), and the function body. For shorthand convenience, if there's only one argument, the parentheses can be left off. Similarly, if the function body is a single expression, the curly braces can be left off, and a "return" statement automatic. Example!
var distanceOldWay = function (x, y) {
return Math.abs(x * x + y * y);
};
var distanceNewWay = (x, y) => Math.abs(x * x + y * y);
The fat arrow also brings in the parent scope's this. Say you have a function that sets up a timer for something:
function exampleClass() {
this.foo = "hello";
}
exampleClass.prototype.start = function (howLong, callback) {
setTimeout(function () {
this.foo = "world";
callback();
}, howLong);
};
When you use the code, you find the that this.foo doesn't work. Whoops, this is actually the global scope (or undefined, in strict mode). Okay, so lets hack it a bit:
exampleClass.prototype.start = function (howLong, callback) {
var that = this;
setTimeout(function () {
that.foo = "world";
callback();
}, howLong);
};
And now, because that got closured into the anonymous function, exampleClass's foo will get set correctly. Fat arrows automatically carry the parent this into the new scope, so the code becomes
exampleClass.prototype.start = function (howLong, callback) {
setTimeout(() => {
this.foo = "world";
callback();
}, howLong);
};
Which is, of course, how we wanted it to work in the first place.
Fat arrow functions take advantage of being entirely new by cleaning house a bit:
- They can't be used as constructors (like exampleClass from above)
- They're immutable (I think this is still in the air though. Probably just means you can't set properties on them.)
- The "arguments" collection is unavailable.
- They throw out the entire prototype chain, making them lighter and faster.
Keeping hope alive
I really don't have much to hope for here. Fat arrow functions are awesome in CoffeeScript, and they're basically the same here. I wouldn't really mind it if they kept the parentheses when there's only one argument. That removes half of the exceptions with the operator, and the other one needs to stay for convenience.
Tl;dr
ECMAScript 6 looks to be full of good changes, but a lot of them feel long overdue. Look for these neat features to show up in Firefox 11+, Chrome 17+, and whatever Internet Explorer we're using in 2016.