The busy JavaScript developer's guide to ECMAScript 6, Part 4

New objects and types in the standard library

Using modules, collections, proxies, and more in the new JavaScript


Content series:

This content is part # of 4 in the series: The busy JavaScript developer's guide to ECMAScript 6, Part 4

Stay tuned for additional content in this series.

This content is part of the series:The busy JavaScript developer's guide to ECMAScript 6, Part 4

Stay tuned for additional content in this series.

Editorial note: This series has been updated with interactive code capabilities. When you see Run on a code listing, it means you can run the code, check the results, make modifications, and run it again.

In the previous articles in this series, you've learned about some of the biggest changes to JavaScript with the ECMAScript 6 specification. If you've been following along, you've sampled a handful of syntactic changes, discovered the functional-like features of the new arrow functions, and experimented with using traditional class syntax in your JavaScript programs. If you're anything like me, you might be feeling pretty relieved to discover that the ECMAScript Technical Committee has managed to make significant changes to everyone's favorite 20-year-old scripting language, without sacrificing its ease of use or prototype-based object system.

This final article in the series introduces a handful of objects and types that are now part of the standard library. Some of these you've definitely used before, either in JavaScript or in other languages, while others might stretch your mind a bit—or even a lot. But they're all worthy additions that I'm betting will find their way into your toolkit over time.


For everyday programming, modules are likely the most obvious library enhancement to ECMAScript 6. Up till now, following the Node.js convention, we've requireed files using a named exports global variable object to describe the values returned. No longer! ECMAScript 6 uses import and export statements to formalize the concept of modules. As you might infer, export is used to declare named values (usually classes or functions, but sometimes variables) from an ECMAScript file, while import is used to pull in those exported names from that file into a different one.

Note: For security reasons, the developerWorks sandbox doesn't let you import files. If you want to try this code, you'll need to create your own output.js file and import it into your code.

Basically, if you have a file that you want to treat as a module—let's call it output—you'll simply export the symbols you want to be able to use elsewhere, like so:

    // output.js
    export function out() {
      console.log("OUT: ", arguments);

Entering the keyword export before the function tells ECMAScript that this file is to be treated as a module. Thereafter, the function will be made available to any other file that imports it:

    import { output } from 'output.js';
    out("I'm using output!");

You might have noticed that the import syntax suffers from one major flaw: in order to use the module, you have to know all the names you wish to import. (Though some might consider this a benefit, because it ensures that no symbols will be imported without the importer's knowledge.) If you want to grab all the exported names from a module, you can use the wildcard (*) import syntax, but then you need to define a module name where you want to scope them:

    import * as Output from 'output.js';
    Output.out("I'm using output!");

Requiring the module name enforces a scoping mechanism; if two modules each exported an out without the module name, the newer one would silently replace its predecessor. Each name entered is wrapped in the module name, further reducing ambiguity.


One of the subtler features introduced with ECMAScript 6 is the new Symbol type. On the surface, it seems rather unremarkable: essentially, an instance of Symbol is a unique name that cannot be duplicated anywhere else. That's it.

Recall that an ECMAScript object is just a collection of name-value pairs, where the value can be either data (strings, numbers, object references, and so on) or behavior (in the form of a function reference). Normally, if you know the name of the thing, you can get at its value, no questions asked.

In some situations, however, the owner of an object needs to be able to ensure that a chosen name doesn't clash with other names. In this case, instead of using a traditional String-based name, you could use a Symbol instance. Symbol ensures the names won't clash.

As an example, let's start with a typical Person type:

Show result

The three object fields are easily accessible to anyone who knows their names. The names are accessible via simple iteration across the contents of the object. While ECMAScript has never been known as a high-security language, this example definitely fails even basic levels of encapsulation.

Using Symbol for access control

Suppose you needed to keep some fields hidden? You could start by making them accessible through Symbol names, rather than standard strings:

Show result

As shown, you can use the Symbol function to create instances of a Symbol. Each of those instances can then be used as the name on the objects in question. If someone tried to access the field using a normal String-based name like firstName, the results would be undefined, because the data doesn't live under that name anymore. Under the new specification, JavaScript won't even show Symbol-based names during standard object iteration. Any attempt to use traditional reflection across the object would essentially fail.

It's also important to note that if someone wanted to add new members to the object from the outside (an example of metaobject programming), the use of the string firstName wouldn't conflict or replace the existing member. This is crucial for libraries and frameworks where it's necessary to add additional behavior or members to existing objects—which is almost every modern framework in use right now.

However, as the last line in Listing 5 shows, if the caller has the Symbol instance, it can be used to access the data just as before, with no hesitation. Unlike the private keyword in other languages, Symbol doesn't quite cut it for enforcing access control.

Member names

JavaScript supports a number of well-known member names, which are useful for creating objects that follow environment-specific patterns. An example would be iterator, which you can use to name functions on objects supporting iteration behavior. If you wanted to create a Fibonacci-generating object that masqueraded as a standard ECMAScript iterator, you would need to create an object with an iterator function on it. Because anybody could be using that name, however, ECMAScript 6 insists that you use the iteratorSymbol instead:

Show result

Again, Symbol's primary function is to help programmers avoid name clashes across libraries. It's a little awkward to grasp at first, but try thinking of Symbol as a unique hash based on the string name it's fed.

Collection types

If you've been using ECMAScript for more than 10 minutes you know that the language supports arrays—it's been a core part of the specification since 1.0. Despite their limitations (fixed-size most of all), arrays have served us well; they'll likely continue to do so for years to come.

But it's time to admit something, just among us, even if we never would say it to anyone else: Arrays . . . can't do everything.

To help pick up the slack, ECMAScript 6 adds two collection types to the standard JavaScript environment: Map and Set.

A Map is a set of name/value pairs, much as ECMAScript objects are. The difference is that a Map contains methods that make it easier to work with than a raw ECMAScript object would be:

  • get() and set() find and set key/value pairs, respectively
  • clear() will empty the collection entirely
  • keys() returns an iterable collection of the keys in the Map
  • values() does the same for the values

Also, like Array, Map includes functional-language-inspired methods like forEach(), which operate on the Map itself.

Show result

Set, meanwhile, looks more like a traditional object collection, in that objects can simply be added to the collection. But Set will examine each object in turn to ensure that it is not a duplicate of a value already present within the collection:

Show result

Like Map, Set has methods on it that allow for functional-style interaction, such as forEach. Fundamentally, Set is like an array, but without the angle brackets. It grows dynamically, and it lacks any sort of ordering mechanism. Using Set, you can't look up an object by index, as you could with an array.

Weak references

ECMAScript 6 also introduces WeakMaps and WeakSets, which are Maps and Sets that hold their values through weak references, rather than the traditional strong reference. The ECMAScript specification best describes what happens to objects held inside the WeakMap; the explanation applies equally to WeakSet:

If an object that is being used as the key of a WeakMap key/value pair is only reachable by following a chain of references that start within that WeakMap, then that key/value pair is inaccessible and is automatically removed from the WeakMap.

I won't get i nto the utility of WeakMaps and WeakSets in this article. They'll mainly be used for library code (particularly with respect to caching), and won't likely show up much in application code.


Asynchronous operations are a core part of the Node.js story (usually under the tagline of event-driven programming) but they've never been easy to implement. At first, the Node.js community seemed to settle on using event subscriptions, but over time developers have migrated to a more callback-driven style. This has brought us the now-dreaded callback hell, wherein Node.js code seems to "march" across the screen:

Show result

This code processes orders for a bookstore that has a single copy of a single book. All of the logic needed to process the order is inline with the multiple nested function calls. If this were doing real work, there could be many lines between each callback, making the code extremely difficult to understand, debug, and maintain.

Also note that for the above example I used an indentation set to two spaces. Imagine the scrolling for developers who insist on four- or eight-space indentations.

After much wailing and gnashing of teeth, the ECMAScript community floated an alternative to callbacks in asynchronous computation: the now standard Promise type.

Using a Promise in JavaScript is two-fold. First, whoever builds the code to "go off and do something" (as shown in the Listing above) will now return Promises rather than using the traditional synchronous execution or the Node.js callback idiom. This enables the caller to use a Promise's then() method to chain sequential calls and catch() to define what should happen in the event of failure:

doSomething.then(function(result) {
  console.log(result); // It did something
}).catch(function(err) {
  console.log(err); // Error

On the doSomething() side, the code can now be written to yield a Promise instance, usually by wrapping a Promise around the function doing the asynchronous execution:

let promise = new Promise(function(resolve, reject) {
  let result = // do a thing, usually async in nature

  if (success) {
  else {
    reject(Error("It didn't work"));

Here's a refactoring of the code to use promises. The logic that was included inline in the listing above is now broken out into separate functions, each of which uses a promise. The result is longer, but much cleaner code:

Show result

The greatly simplified processBookOrders() function at the top of the listing makes it clear what the code does. It gets the next order, it checks to see if the store carries the book, then it checks the inventory level for that book. If any of the functions invoked here (getNextOrder(), doWeCarryThisBook(), or isItInStock()) returns a JavaScript Error object via its reject clause, the function stops. Otherwise the function goes on to the next then statement. The ability to chain multiple processes is very powerful.

Try changing the values of bookOrder.isbn or bookOrder.qty or change the code in getNextOrder() to read success = false. The order will fail, returning the appropriate Error object.

We're clearly in "your mileage will vary" territory here. In my experience developers will use Promises handed back from libraries, so I expect that most will start by using rather than constructing them. Over time, more developers will likely build their own Promises for use by other modules.

There's more to be said about Promises, but it will have to happen in another article. Fortunately for those who don't want to use a thing until they've explored it in depth, raw callbacks and events aren't going away, so you don't need to adopt this feature right away.

Dynamic proxies

JavaScript programming with dynamic proxies was already popular, but ECMAScript 6 standardized the new Proxy type. Having a standardized method helps us avoid accidental clashes and/or confusion across libraries. In essence, a Proxy implements "interception" behavior, enabling one object to stand in front of another. The intercepting object then gets first crack at any method calls or property references intended for the original target.

Being able to replace an object's method with another definition is nothing new for ECMAScript, but the Proxy type does so much more. It can even intercept requests that don't exist on a target object—think method calls, property references, and the like.

An example is worth a thousand words, so let's spin up some code. It's conventional to use method-call logging to demonstrate the power of proxies, so I'll follow along. We'll go back to the Person class we've used throughout these articles.

I'll stick a few methods of interest on my Person object after construction, just for fun. Adding these here will also show that dynamic proxies can work with any ECMAScript object, regardless of how it was constructed or defined:

Show result

We now have two methods: sayHowdy(), which takes no parameters, and waveGoodbye(), which takes one parameter and returns a result. By themselves, they're not all that interesting, but they'll do to represent the methods we want to trap. Each time one of these methods is invoked, we want to see a message in the console that says "method invoked," ideally along with some interesting information about the method call.

While we're at it, wouldn't it be nice to be able to see when properties are accessed? We could use this feature to either retrieve the value or set it. Proxies can do that, as well as intercepting constructor invocations and other less commonly used invocations. In essence, a proxy can intercept anything you can do to an object, giving you the opportunity to do something else, like slip in some additional behavior.

Using proxies in ECMAScript 6

To set up a proxy, we first need to identify the target, also known as the object whose methods or properties we want to intercept. In this case we'll intercept the Person object. Next, we need to identify the behavior we want to insert between the caller and target. In ECMAScript parlance, this is called the handler. Each handler defines one or more of the following methods:

  • get() and set() intercept any attempt to find or set the value of a property, respectively. (Remember, an object's functions are properties too.)
  • apply() intercepts any call to a function
  • has() intercepts the in operator
  • ownKeys() intercepts the Object method getOwnPropertyNames()
  • getPrototypeOf(), setPrototypeOf(), isExtensible(), preventExtensions(), defineProperty(), and getOwnPropertyDescriptor() intercept the Object methods of the same names
  • deleteProperty() intercepts the delete operator

We'll start by trapping any requests to get and set properties on the Person (target) object. We do that by creating a handler object that provides methods named set and get, respectively:

Show result

Notice how the set handler not only logs the message to the console, but also does the work of property assignment. This is necessary because the handler is completely in control: if you don't assign the property to the target, it won't be set. The same is true for the get handler, which must return the target's property value. If you don't assign the property, nothing (or, rather, undefined) will be the only property returned.

The last step is to wire up a Proxy object around the target and the handler. We capture the Proxy object back into the original variable with the following code:
ted = new Proxy(ted, handler);

In certain scenarios, you'll want to hold on to the original, so you can access the target without having to go through an interceptor. Most of the time, however, you'll use the Proxy as a silent processor, such that clients using the target object don't even realize there's anything between them and the target.

If you added waveGoodbye() and sayHowdy() to the object after the handler was in place, the handler would be invoked for the property-set operations. This is because waveGoodbye() and sayHowdy() are technically properties of the function type. Run the code to see how the handler's get and set functions are invoked whenever a property or function is accessed. Try moving the sayHowdy() and sayGoodbye() functions after the definition of the handler and run the code again.

Note that the get() handler is invoked. Accessing the method means obtaining that method (so as to invoke it) and then (in the case of sayHowdy) obtaining the values for any properties referenced in that method. The set() method is invoked when the favoriteColor property is created, even though that property didn't exist when then handler was created. Accessing the new property triggers the get() method, as you would expect.

A proxy handler on function

To be clear, the get handler is always being invoked, regardless of how the property was defined. To illustrate this, we'll define a method called eat() in the Person class.

If the Person-typed object has its eat() method invoked, ECMAScript's first task is to resolve the property name "eat" as a property that yields a function. First it will first obtain that function, then immediately invoke it. If we want to see more details about the function being invoked, we'll need to slip a new handler into the middle of the invocation process, after the function has been located and returned. The easiest way is to return a function that wraps the original function:

Show result

The procedure might look complicated, but it's really not. If the property being accessed is something other than a function, just get the result and return it. If the property is a function, create a function literal and return that instead. The function literal returned will invoke the original function. Using ECMAScript's apply method on function objects ensures that we bind the right this into place. (If we used this literally, instead of target, the this would be the handler, not the target.)

Easy-peasy, right?

Actually, if you've never seen this type of code before, it's pretty mind-blowing. Using Proxy, you can do things like type-safe property validation (write a handler that ensures that values being set are of the right type for a given property); remote execution (return a proxy that knows how to make remote calls via an HTTP API, serializing the arguments into a JSON array and deserializing the results); or even putting in authorization boundaries (wrapping a domain object with a proxy that will check a given user's credentials in memory). Formally, all of these uses fall under the heading of aspect-oriented programming. Together they introduce a whole new world of thinking about how to capture concerns in JavaScript.


ECMAScript 6 is by far the most ambitious revision of JavaScript to date, which inevitably requires an adjustment period. ECMAScript interpreters haven't all caught up to the specification yet. Don't be surprised if your code sometimes fails; just check your interpreter to see what's not supported and adjust your code as needed.

Remember, too, that not all is lost if your code won't run: you can use one of the popular Node.js transpilers to tame your code into a somewhat less-bleeding-edge ECMAScript.

The beautiful thing is how the ECMAScript Technical Committee has pushed the language forward, while still enabling considerable backward compatibility. Because of this you can take a baby steps approach to adopting ECMAScript 6: just pick a feature you like and integrate it into your code. Once you're comfortable with that, you can pick up the next thing you want to try, and so on. You never have to wade in deeper than you—or your productivity—can handle, but you are encouraged to keep going forward. Gradually, you can start taking advantage of the many powerful new features and conventions that are part of standard JavaScript.

That wraps this series, so without further ado, I'll simply say . . .

    return "Enjoy!";

Catch you next time!

Downloadable resources

Related topics


Sign in or register to add and subscribe to comments.

Zone=Web development
ArticleTitle=The busy JavaScript developer's guide to ECMAScript 6, Part 4: New objects and types in the standard library