ES6 Features
Updated:
I took a refresher to learn about ES6 and the new language niceties (Source). Note that these are relative to my previous experience with Javascript, circa 2014. And also I’ve been writing python for the last 3 years.
Const, Let, and Var
var
will hoist the variable, which moves it to the root of execution context.
This is the enclosing function or global.
This is like a header file in C, where all the declarations are interpreted first, before any code is ran.
Many other languages don’t do this (implicitly), so it’s a gotcha.
const
and let
use block-scope instead.
Use const
and let
instead of var
for declaration.
Because the scope due to hoisting is unexpectedly wide, there are many gotchas that arise:
- Variable can be used before it’s “declared”.
Instead of throwing error about referencing an undefined variable (not to be confused with javascript
undefined
value) - For loop variable will persist after loop completes (note: this exists in python)
- Each iteration of a loop is the same execution context, so closures refer to the same variable reference. Block-scope makes separate variables for each iteration.
Array Functions
There are a full set of methods on Array
now.
Now more Array.forEach(myActualArray)
shenanigans.
forEach
, to loopmap
, for transformingfilter
, for getting subset based on predicatefind
, for getting the first element offilter
every
, for checking predicate on all elementssome
, for checking that at least one element matches predicatereduce
, to get single value after going through all items
Arrow Functions
Arrow functions do exactly what you expect if you come from other languages (Python, Java, C#), with respect to this
handling.
It correctly binds to the correct context, by default.
And we all love sane defaults.
This was a major point of confusion for me, the need to use apply()
or bind()
.
There’s probably a good reason, based on the language design.
But I’m not looking to design language or interpreter, I just want my damn code to work.
I’ll admit that I still don’t understand it but the point is I don’t need to.
And my refusal to learn has zero detriment to 99% of my use cases of javascript functions.
Classes
Javascript is a protoypical language. In other languages, you declare classes and instantiate them. In Javascript, classes are objects and you clone them to “instantiate”.
ES6 has class syntax, which is simply syntactic sugar over this concept. While the mechanics are the same under the hood, this sugar is great for developers to keep in context. This is a good example of abstraction: developers don’t need to know about the nuances of prototyping vs. instantiation (for majority of cases) and it makes little difference on the outcome. If they need to leverage the features, it’s still accessible, albeit without syntactical sugar.
Object Literals
Object literals have some syntactic sugar too. If there’s only a value and no key, it’s implicitly the string name of the variable.
const user = 'bob';
const visit = {
user;
};
console.log(visit.user);
Calculated properties are also available. This just means the key name is dynamic.
const visit = {
['foo' + 'bar']: 1,
}
Note: I realized this contrived example is bad because string concatenation of literals probably gets lexically parsed away to a single token. But the idea is there.
Template Strings
FINALLY.
When I started using javascript and wanted to format strings, I looked for a string.format()
or a printf
function.
Only to discover that people were hand-rolling their own adhoc functions.
Literally everyone was copy-pasting the same function.
I could use a library but isn’t that what a standard library is for?
Shit that I need every day and are probably going ot import into my project by default.
Template strings make the most mundane, yet tedious, task no longer a valid gripe of mine. In fact, it skips an evolutionary step altogether and is a language construct, not simply a function.
const word = 'cool';
console.log(`I can print {word} things.`);
These operate the same as f-strings
in python 3.
They evaluate and substitute expressions.
The same best practices would pertain here: don’t embed lots of logic, only minimal presentation-related logic.
Default Arguments
Huh, I didn’t know Javascript didn’t allow for this before. I wonder if it was trying to be like Java and encourage function overloading instead.
Parameter defaults are just like in Python.
const substring = (string, startIndex = 0, length = -1) => {
};
Rest and Spread
Rest operator is how to handle variadic parameters.
In python, this is*args
for positional params and *kwargs
for keyword params.
const substring = (string, ...others) => {
};
The easy to use and reads syntax makes it feasible to consider writing forwards compatible functionality. I’m not entirely sure how useful it will be in real-world.
Spread operator allows for iterable object to expand into positional params (reverse of rest operator).
const param = [1, 2, 3];
substring('test string', ...param) == substring('test string', 1, 2, 3);
Destructuring
This is really cool. It’s similar to tuple-unpacking in Python but more powerful.
const printFirstName = ({firstName}) => {
console.log(firstName);
};
const user = { firstName: 'John', lastName: 'Nguyen'};
printFirstName(user);
The function requires an object for param and only gets the target property from it. This reduces a lot of boilerplate noise that arises from continually dereferencing properties.
You can even use the rest
operator to pluck out single attributes!
This is more commonly useful in JSX to pass remainder of props to child.
const printNotFirstName = ({firstName, ...rest}) => {
console.log(rest);
};
const user = { firstName: 'John', lastName: 'Nguyen'};
printFirstName(user);
It also works for arrays, this is more traditional tuple-unpacking.
const printFullName = ([firstName, , lastName]) => {
console.log(`${firstName} ${lastName}`);
};
printFullName(['John', 'Something', 'Nguyen', 'Jr.', 'the First']);
Promises
I’m still trying to wrap my head around promises. It’s a syntax style for writing asynchronous code for event-loop processing. With event-driven programming, there tends to be a lot of callbacks, which in turn have callbacks. To avoid all the nesting, promises are a form of “sugar” using words “then”, “reject”, “resolve”, “catch”, etc.
const user = callService('get_user').then(response => {
return response.userId;
}).catch(response => {
if (response.timeout) {
// Service timeout
throw TimeoutError();
} else {
// User doesn't exist
return "Guest";
}
}).then( user => {
logUser(user)
});
// Old way
const user = callService('get_user', response => {
let user;
if (response.okay) {
user = response.userId;
} else if (response.Timeout) {
throw TimeoutError();
} else {
user = 'Guest';
}
logUser(user);
return user;
});
return response.userId;
}).catch(response => {
if (response.timeout)
// User doesn't exist
return "Guest";
}
})
I haven’t illustrated how unreadable it can get but hopefully this is enough to get your mind started thinking about it.