The ES2017 Guide
By Flavio Copes
Learn the features added in ES2017: string padding, Object values, entries and descriptors, trailing commas, async functions, and shared memory.
ECMAScript 2017 is the eighth edition of the JavaScript language specification.
It is also called ES2017 and sometimes ES8.
The edition added:
String.prototype.padStart()String.prototype.padEnd()Object.values()Object.entries()Object.getOwnPropertyDescriptors()- trailing commas in function parameters and calls
- async functions
- shared memory and
Atomics
String padding
padStart() adds text to the beginning of a string until it reaches a target length:
const value = '42'
value.padStart(5, '0') //'00042'
padEnd() adds text to the end:
const label = 'Name'
label.padEnd(10, '.') //'Name......'
The padding string defaults to a space:
'test'.padStart(6) //' test'
If the original string is already long enough, the method returns it unchanged.
The methods repeat and truncate the padding as needed:
'test'.padStart(9, 'ab') //'ababatest'
The target uses JavaScript string length, which counts UTF-16 code units rather than user-perceived characters.
Object.values()
Object.values() returns the values of an object’s own enumerable string-keyed properties:
const person = {
name: 'Flavio',
age: 42,
}
Object.values(person) //['Flavio', 42]
It does not include inherited, non-enumerable, or Symbol-keyed properties.
The values use the same property order as Object.keys().
Object.entries()
Object.entries() returns [key, value] pairs:
const person = {
name: 'Flavio',
age: 42,
}
Object.entries(person)
//[['name', 'Flavio'], ['age', 42]]
This makes an object easy to loop over:
for (const [key, value] of Object.entries(person)) {
console.log(`${key}: ${value}`)
}
Like Object.values(), it includes only own enumerable string-keyed properties.
Convert those entries to a Map:
const map = new Map(Object.entries(person))
Convert entries back to an object with Object.fromEntries(). That companion method arrived later, in ES2019.
Object.getOwnPropertyDescriptors()
Every object property has a descriptor.
A data property descriptor can contain:
valuewritableenumerableconfigurable
An accessor property uses get and set instead of value and writable.
Inspect every own property descriptor:
const person = {
get name() {
return 'Flavio'
},
}
console.log(
Object.getOwnPropertyDescriptors(person),
)
Unlike Object.keys(), this method includes non-enumerable and Symbol-keyed own properties.
It is useful when cloning an object’s properties without turning getters into plain values:
const clone = Object.create(
Object.getPrototypeOf(person),
Object.getOwnPropertyDescriptors(person),
)
This is still a shallow clone. Values referenced by the descriptors are not recursively copied.
Object.assign() and object spread copy enumerable values. They do not preserve complete property descriptors.
Trailing commas in functions
Arrays and objects already allowed trailing commas. ES2017 added them to function parameter lists and calls:
function createUser(
name,
role,
) {
return {
name,
role,
}
}
And:
createUser(
'Flavio',
'author',
)
This produces smaller version-control changes when adding another line.
A trailing comma is not allowed after a rest parameter:
function collect(...values) {
return values
}
Async functions
An async function always returns a promise:
async function getName() {
return 'Flavio'
}
getName().then(console.log)
The returned promise resolves to 'Flavio'.
Use await inside an async function to wait for a promise:
async function loadPost() {
const response = await fetch('/api/posts/42')
if (!response.ok) {
throw new Error(`Request failed: ${response.status}`)
}
return response.json()
}
await pauses that async function. It does not block the JavaScript thread.
An error thrown inside the function rejects its returned promise:
async function showPost() {
try {
const post = await loadPost()
console.log(post.title)
} catch (error) {
console.error(error)
}
}
Sequential awaits are correct when one operation depends on the previous one.
Start independent work together with Promise.all():
async function getJson(url) {
const response = await fetch(url)
if (!response.ok) {
throw new Error(`Request failed: ${response.status}`)
}
return response.json()
}
async function loadPage() {
const [post, comments] = await Promise.all([
getJson('/api/posts/42'),
getJson('/api/posts/42/comments'),
])
return {
post,
comments,
}
}
This avoids waiting for the first independent request before starting the second.
See the dedicated async and await guide for more examples.
Shared memory and Atomics
ES2017 introduced SharedArrayBuffer and the Atomics object.
A SharedArrayBuffer lets workers access the same memory instead of copying data through messages:
const buffer = new SharedArrayBuffer(4)
const counter = new Int32Array(buffer)
Atomics.add(counter, 0, 1)
Atomics.add() updates the value as one atomic operation and returns its previous value.
Atomics are needed because several agents can access shared memory at the same time. Normal reads and writes do not provide the same synchronization guarantees.
This is an advanced, low-level API. Message passing is simpler for most worker tasks.
Browser security requirements
After speculative-execution security issues were discovered, browsers restricted shared memory.
A document normally needs to be cross-origin isolated before it can create and send a SharedArrayBuffer. Check:
if (crossOriginIsolated) {
const buffer = new SharedArrayBuffer(4)
}
Cross-origin isolation typically requires these response headers:
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp
Those headers affect which cross-origin resources a page can load. Test the complete application before enabling them.
Atomics.wait() blocks and cannot be used on the browser’s main thread. Use it in a worker where supported.
See the official references for ECMAScript 2017, Object.values(), Object.entries(), async functions, SharedArrayBuffer, and Atomics.
Related posts about js: