Overview Of ECMAScript 2017 – ES2017 – “ES8” Features
Hello, JavaScript enthusiasts!
ECMAScript 2017, also known as ES8, introduced several important features and improvements to JavaScript. Here’s a rundown of the key features:
Some of the key features introduced in ES8 include:
- async & await function
- Object.entries()
- Object.values()
- Object.getOwnPropertyDescriptors()
- Trailing Commas
- string.padEnd() and string.padStart() functions
- Shared Memory and Atomics
1. async & await function
The async
and await
keywords, introduced in ECMAScript 2017 (ES8), offer a cleaner and more intuitive way to handle asynchronous operations compared to traditional methods such as callbacks and Promises. By allowing you to write asynchronous code that looks and behaves like synchronous code, async
and await
improve readability and simplify error handling.
Here’s a brief overview of how async
and await
work:
async
Functions
An async
function always returns a Promise. Within an async
function, you can use the await
keyword to pause execution until a Promise is resolved or rejected, simplifying the handling of asynchronous operations.
async function fetchData() {
return "Data fetched"; // This is equivalent to returning a Promise.resolve("Data fetched")
}
await
Expressions
The await
keyword can only be used inside async
functions. It pauses the function’s execution until the Promise resolves or rejects, returning the resolved value or throwing an error if the Promise is rejected.
async function fetchData() {
let response = await fetch('https://api.example.com/data');
let data = await response.json();
return data;
}
Example: Using async
and await
Here’s a complete example showing how to use async
and await
to fetch data from an API and handle errors:
async function fetchData(url) {
try {
let response = await fetch(url);
// Check if response is ok (status in the range 200-299)
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
let data = await response.json();
return data;
} catch (error) {
console.error('Error fetching data:', error);
throw error; // Rethrow the error to be handled by the caller if needed
}
}
// Example usage:
fetchData('https://api.example.com/data')
.then(data => console.log(data))
.catch(error => console.error('Caught error:', error));
2. Object.entries()
The Object.entries()
method, introduced in ECMAScript 2017 (ES8), offers a convenient way to work with objects in JavaScript. It returns an array of the object’s own enumerable string-keyed [key, value]
pairs.
Here’s a basic example demonstrating how Object.entries()
works:
const obj = {
name: 'Alice',
age: 30,
job: 'Engineer'
};
const entries = Object.entries(obj);
console.log(entries);
// Output: [ ['name', 'Alice'], ['age', 30], ['job', 'Engineer'] ]
Iterating Over Object Properties
You can use Object.entries()
with for...of
loops to iterate over the properties of an object:
const person = {
name: 'Bob',
age: 25,
city: 'New York'
};
for (const [key, value] of Object.entries(person)) {
console.log(`${key}: ${value}`);
}
// Output:
// name: Bob
// age: 25
// city: New York
Transforming Objects
You can use Object.entries()
in combination with Array.prototype.map()
to transform an object:
const original = {
a: 1,
b: 2,
c: 3
};
const transformed = Object.entries(original).map(([key, value]) => [key, value * 2]);
console.log(Object.fromEntries(transformed));
// Output: { a: 2, b: 4, c: 6 }
Combining with Object.fromEntries()
You can convert an object to an array of entries, transform it, and then convert it back to an object using Object.fromEntries()
:
const obj = {
x: 1,
y: 2,
z: 3
};
const updatedObj = Object.fromEntries(
Object.entries(obj).map(([key, value]) => [key, value * 10])
);
console.log(updatedObj);
// Output: { x: 10, y: 20, z: 30 }
3. Object.values()
The Object.values()
method, introduced in ECMAScript 2017 (ES8), retrieves an array of the object’s own enumerable property values. It is useful for working with the values of an object without needing to access the keys.
Here’s a basic example demonstrating how Object.values()
works:
const person = {
name: 'Alice',
age: 30,
job: 'Engineer'
};
const values = Object.values(person);
console.log(values);
// Output: ['Alice', 30, 'Engineer']
Iterating Over Values
You can use Object.values()
in conjunction with array methods like forEach()
to iterate over the values:
const numbers = {
one: 1,
two: 2,
three: 3
};
Object.values(numbers).forEach(value => {
console.log(value);
});
// Output:
// 1
// 2
// 3
Combining with Other Methods
Object.values()
can be used with other array methods to transform data. For example, you can use it with map()
to create a new array of modified values:
const data = {
a: 1,
b: 2,
c: 3
};
const doubledValues = Object.values(data).map(value => value * 2);
console.log(doubledValues);
// Output: [2, 4, 6]
Combining with Object.entries()
and Object.fromEntries()
You can combine Object.values()
with Object.entries()
and Object.fromEntries()
to perform more complex transformations. For instance, you can create a new object by manipulating the values of an existing object:
const obj = {
a: 1,
b: 2,
c: 3
};
const updatedObj = Object.fromEntries(
Object.entries(obj).map(([key, value]) => [key, value + 10])
);
console.log(updatedObj);
// Output: { a: 11, b: 12, c: 13 }
In this example, Object.entries()
retrieves the entries of the object, map()
modifies the values, and Object.fromEntries()
reconstructs the object with the updated values.
4. Object.getOwnPropertyDescriptors()
The Object.getOwnPropertyDescriptors()
method, introduced in ECMAScript 2017 (ES8), retrieves all property descriptors of an object. These descriptors include metadata about the properties, such as whether they are writable, enumerable, and configurable.
Here’s an example showing how Object.getOwnPropertyDescriptors()
works:
const obj = {
name: 'Alice',
age: 30
};
// Define a non-enumerable property
Object.defineProperty(obj, 'secret', {
value: 'hidden',
enumerable: false,
writable: true,
configurable: true
});
const descriptors = Object.getOwnPropertyDescriptors(obj);
console.log(descriptors);
/*
Output:
{
name: { value: 'Alice', writable: true, enumerable: true, configurable: true },
age: { value: 30, writable: true, enumerable: true, configurable: true },
secret: { value: 'hidden', writable: true, enumerable: false, configurable: true }
}
*/
5. Trailing Commas
Trailing commas, also known as “final commas,” were officially standardized in ECMAScript 2017 (ES8). They are commas that appear after the last item in an array or object literal. Their use was introduced to simplify code maintenance and avoid syntax errors when modifying lists of items.
You can place a trailing comma after the last element in an array:
const fruits = [
'apple',
'banana',
'cherry', // Trailing comma is allowed here
];
Example: Adding Elements
Without Trailing Commas
const numbers = [
1,
2,
3 // No trailing comma here
];
Adding another number would require adding a comma to the previous last element:
const numbers = [
1,
2,
3, // Added comma
4
];
With Trailing Commas
const numbers = [
1,
2,
3, // Trailing comma is allowed here
];
Adding another number doesn’t require modifying the previous line:
const numbers = [
1,
2,
3, // Trailing comma remains
4
];
Example: Removing Elements
Without Trailing Commas
const numbers = [
1,
2,
3 // No trailing comma here
];
Removing the last number will not require any changes to the remaining lines:
const numbers = [
1,
2 // Trailing comma remains
];
With Trailing Commas
const numbers = [
1,
2,
3, // Trailing comma remains here
];
Removing the last number also doesn’t require changes to the remaining lines:
const numbers = [
1,
2, // Trailing comma remains
];
Browser and Environment Support
Most modern browsers and JavaScript environments fully support trailing commas. It’s a good practice to use trailing commas in new code, and it is also supported in recent versions of Node.js and other JavaScript engines.
6. string.padEnd() and string.padStart() functions
The String.prototype.padEnd() and String.prototype.padStart() methods were introduced in ECMAScript 2017 (ES8). They provide a way to pad strings to a specific length with a specified string, which can be useful for formatting output or aligning text.
String.prototype.padEnd()
The padEnd()
method pads the current string with another string (or characters) until it reaches the specified length. The padding is applied at the end of the original string.
const str = 'Hello';
console.log(str.padEnd(10, '!'));
// Output: 'Hello!!!!!'
console.log(str.padEnd(10));
// Output: 'Hello ' (pads with spaces by default)
String.prototype.padStart()
The padStart()
method pads the current string with another string (or characters) until the resulting string reaches the specified length. The padding is applied at the start of the original string.
const str = 'Hello';
console.log(str.padStart(10, '!'));
// Output: '!!!!!Hello'
console.log(str.padStart(10));
// Output: ' Hello' (pads with spaces by default)
Use Cases
Formatting Output: Useful for formatting strings in a tabular format, aligning columns, or creating uniformly-sized fields.
const name = 'Alice';
const age = '30';
console.log(name.padEnd(10) + age.padStart(5));
// Output: 'Alice 30'
Creating Fixed-Length Strings: Useful when you need strings of a specific length, such as in text-based UI elements or file names.
const id = '7';
const paddedId = id.padStart(5, '0');
console.log(paddedId);
// Output: '00007'
User Interfaces: Padding strings can help align text or numbers in user interfaces, such as formatting dates, times, or other data for display.
7. Shared Memory and Atomics
Shared Memory and Atomics were introduced in ECMAScript 2017 (ES8) as part of the SharedArrayBuffer
and Atomics
APIs. These features provide advanced capabilities for handling shared memory and synchronization between threads in JavaScript, which is particularly useful in web workers and other concurrent environments.
SharedArrayBuffer
SharedArrayBuffer
is a type of ArrayBuffer that allows memory to be shared between different threads. This means that multiple threads (such as Web Workers) can access and modify the same memory space concurrently.
Creating a SharedArrayBuffer
You create a SharedArrayBuffer
with a specified byte length, similar to how you create a regular ArrayBuffer
:
const sharedBuffer = new SharedArrayBuffer(1024); // 1 KB buffer
Using SharedArrayBuffer
To use the SharedArrayBuffer
, you typically work with typed arrays like Int32Array
or Float64Array
, which provides a way to interact with the memory:
const sharedBuffer = new SharedArrayBuffer(1024);
const int32View = new Int32Array(sharedBuffer);
int32View[0] = 42; // Set value in shared memory
console.log(int32View[0]); // Get value from shared memory
Atomics API
The Atomics
object provides atomic operations for working with shared memory. These operations are designed to be safe from race conditions and are useful for managing concurrency.
Atomics Methods
Here are some commonly used methods in the Atomics
API:
Atomics.add()
: Adds a value to an element of a TypedArray
and returns the original value.
Atomics.add(int32View, 0, 1); // Adds 1 to the first element
Atomics.load()
: Loads a value from a TypedArray
.
const value = Atomics.load(int32View, 0); // Reads the value at index 0
Atomics.store()
: Stores a value in a TypedArray
.
Atomics.store(int32View, 0, 42); // Stores 42 at index 0
Atomics.compareExchange()
: Compares the value at a specified index with a given value and, if they are equal, replaces it with a new value.
const oldValue = Atomics.compareExchange(int32View, 0, 42, 100); // If value at index 0 is 42, replace it with 100
Atomics.wait()
: Waits for a specified value at a given index to change. This method can be used for synchronization.
Atomics.wait(int32View, 0, 0); // Waits for the value at index 0 to change from 0
Atomics.wake()
: Wakes up threads that are waiting on a specific index.
Atomics.wake(int32View, 0, 1); // Wakes up 1 thread waiting on index 0
Example: Shared Memory with Web Workers
Here’s an example of how you might use SharedArrayBuffer
and Atomics
with Web Workers to share and synchronize data between a main thread and a worker:
Main Thread:
const sharedBuffer = new SharedArrayBuffer(4);
const int32View = new Int32Array(sharedBuffer);
// Create a worker and pass the shared buffer
const worker = new Worker('worker.js');
worker.postMessage(sharedBuffer);
// Perform operations on the shared buffer
Atomics.store(int32View, 0, 1);
console.log('Main thread value:', Atomics.load(int32View, 0));
Worker (worker.js
):
self.onmessage = function(e) {
const sharedBuffer = e.data;
const int32View = new Int32Array(sharedBuffer);
// Wait until the value in the shared buffer is 1
Atomics.wait(int32View, 0, 1);
console.log('Worker thread value:', Atomics.load(int32View, 0));
// Modify the shared buffer
Atomics.store(int32View, 0, 2);
};
If you missed my recent posts on ES6 (ECMAScript 2015) and ES7 (ECMAScript 2016), you can catch up by clicking the links below to explore their features and updates.
Click here to explore ES6 (ECMAScript 2015)
Click here to explore ES7 (ECMAScript 2016)
Happy coding!
FAQs
How does async/await work in ECMAScript 2017?
async
and await
simplify working with Promises. An async
function returns a Promise, and within it, await
can be used to pause execution until the Promise is resolved or rejected. This makes asynchronous code easier to read and maintain.
async function fetchData() {
let response = await fetch('https://api.example.com/data');
let data = await response.json();
console.log(data);
}
What is the difference between Object.entries() and Object.values()?
Object.entries(obj)
returns an array of[key, value]
pairs for each property in the object.Object.values(obj)
returns an array of the values of the object’s properties.
let obj = { a: 1, b: 2 };
console.log(Object.entries(obj)); // [['a', 1], ['b', 2]]
console.log(Object.values(obj)); // [1, 2]
How do String.prototype.padStart() and String.prototype.padEnd() work?
padStart(targetLength, padString)
pads the current string with another string (padString) until the resulting string reaches the given length (targetLength). Padding is applied from the start of the string.padEnd(targetLength, padString)
pads the current string with another string (padString) until the resulting string reaches the given length (targetLength). Padding is applied from the end of the string.
let str = '5';
console.log(str.padStart(4, '0')); // '0005'
console.log(str.padEnd(4, '0')); // '5000'
What is Object.getOwnPropertyDescriptors() used for?
Object.getOwnPropertyDescriptors(obj)
returns an object containing all own property descriptors of the specified object. This is useful when you need to clone or manipulate property descriptors.
let obj = { a: 1 };
let descriptors = Object.getOwnPropertyDescriptors(obj);
console.log(descriptors);
// Output: { a: { value: 1, writable: true, enumerable: true, configurable: true } }
What are SharedArrayBuffer and Atomics in ES8?
SharedArrayBuffer
allows the creation of shared memory between different threads or workers.Atomics
is a global object providing atomic operations for synchronization with shared memory.
These features are typically used in Web Workers for advanced concurrency and parallel processing.
let sharedBuffer = new SharedArrayBuffer(1024);
let uint8Array = new Uint8Array(sharedBuffer);
Atomics.store(uint8Array, 0, 42); // Store 42 at index 0 atomically
console.log(Atomics.load(uint8Array, 0)); // Load value from index 0
Are there any known issues or limitations with ECMAScript 2017 features?
While ES8 features greatly enhance JavaScript, they can have compatibility issues with older environments or browsers that do not fully support ES8. Additionally, SharedArrayBuffer
and Atomics
might be restricted in certain environments due to security concerns related to cross-origin issues.
How can I check if my environment supports ECMAScript 2017 features?
You can use feature detection libraries like Babel to transpile code to ensure compatibility with older environments, or check the compatibility tables on MDN Web Docs or Can I use for specific feature support.
5 thoughts on “Overview Of ECMAScript 2017 – ES2017 – “ES8” Features”