ES6 in Action: Symbols and Their Uses
While ES2015 has introduced many language features that have been on developers’ wish lists for some time, there are some new features that are less well known and understood, and the benefits of which are much less clear — such as symbols.
The symbol is a new primitive type, a unique token that’s guaranteed never to clash with another symbol. In this sense, you could think of symbols as a kind of UUID (universally unique identifier). Let’s look at how symbols work, and what we can do with them.
Creating New Symbols
Creating new symbols is very straightforward and is simply a case of calling the Symbol function. Note that this is a just a standard function and not an object constructor. Trying to call it with the new
operator will result in a TypeError
. Every time you call the Symbol
function, you’ll get a new and completely unique value.
const foo = Symbol();
const bar = Symbol();
foo === bar
// <-- false
Symbols can also be created with a label, by passing a string as the first argument. The label doesn’t affect the value of the symbol, but is useful for debugging, and is shown if the symbol’s toString()
method is called. It’s possible to create multiple symbols with the same label, but there’s no advantage to doing so and this would probably just lead to confusion.
let foo = Symbol('baz');
let bar = Symbol('baz');
foo === bar
// <-- false
console.log(foo);
// <-- Symbol(baz)
What Can I Do With Symbols?
Symbols could be a good replacement for strings or integers as class/module constants:
class Application {
constructor(mode) {
switch (mode) {
case Application.DEV:
// Set up app for development environment
break;
case Application.PROD:
// Set up app for production environment
break;
case default:
throw new Error('Invalid application mode: ' + mode);
}
}
}
Application.DEV = Symbol('dev');
Application.PROD = Symbol('prod');
// Example use
const app = new Application(Application.DEV);
String and integers are not unique values; values such as the number 2
or the string development
, for example, could also be in use elsewhere in the program for different purposes. Using symbols means we can be more confident about the value being supplied.
Another interesting use of symbols is as object property keys. If you’ve ever used a JavaScript object as a hashmap (an associative array in PHP terms, or dictionary in Python) you’ll be familiar with getting/setting properties using the bracket notation:
const data = [];
data['name'] = 'Ted Mosby';
data['nickname'] = 'Teddy Westside';
data['city'] = 'New York';
Using the bracket notation, we can also use a symbol as a property key. There are a couple of advantages to doing so. First, you can be sure that symbol-based keys will never clash, unlike string keys, which might conflict with keys for existing properties or methods of an object. Second, they won’t be enumerated in for … in
loops, and are ignored by functions such as Object.keys()
, Object.getOwnPropertyNames()
and JSON.stringify()
. This makes them ideal for properties that you don’t want to be included when serializing an object.
const user = {};
const email = Symbol();
user.name = 'Fred';
user.age = 30;
user[email] = '[email protected]';
Object.keys(user);
// <-- Array [ "name", "age" ]
Object.getOwnPropertyNames(user);
// <-- Array [ "name", "age" ]
JSON.stringify(user);
// <-- "{"name":"Fred","age":30}"
It’s worth noting, however, that using symbols as keys doesn’t guarantee privacy. There are some new tools provided to allow you to access symbol-based property keys. Object.getOwnPropertySymbols()
returns an array of any symbol-based keys, while Reflect.ownKeys()
will return an array of all keys, including symbols.
Object.getOwnPropertySymbols(user);
// <-- Array [ Symbol() ]
Reflect.ownKeys(user)
// <-- Array [ "name", "age", Symbol() ]
Continue reading %ES6 in Action: Symbols and Their Uses%
LEAVE A REPLY
You must be logged in to post a comment.