# Symbol type By specification, object property keys may be either of string type, or of symbol type. Not numbers, not booleans, only strings or symbols, these two types. Till now we've only seen strings. Now let's see the advantages that symbols can give us. [cut] ## Symbols "Symbol" value represents an unique identifier with a given name. A value of this type can be created using `Symbol(name)`: ```js // id is a symbol with the name "id" let id = Symbol("id"); ``` Symbols are guaranteed to be unique. Even if we create many symbols with the same name, they are different values. For instance, here are two symbols with the same name -- they are not equal: ```js run let id1 = Symbol("id"); let id2 = Symbol("id"); *!* alert(id1 == id2); // false */!* ``` If you are familiar with Ruby or another language that also has some sort of "symbols" -- please don't be misguided. JavaScript symbols are different. ````warn header="Symbols don't auto-convert to a string" Most values in JavaScript support implicit conversion to a string. For instance, we can `alert` almost any value, and it will work. Symbols are special. They don't auto-convert. For instance, this `alert` will show an error: ```js run let id = Symbol("id"); *!* alert(id); // TypeError: Cannot convert a Symbol value to a string */!* ``` If we really want to show a symbol, we need to call `.toString()` on it, like here: ```js run let id = Symbol("id"); *!* alert(id.toString()); // Symbol(id), now it works */!* ``` That's a "language guard" against messing up, because strings and symbols are fundamentally different and should not occasionally convert one into another. ```` ## "Hidden" properties Symbols allow us to create "hidden" properties of an object, that no other part of code can occasionally access or overwrite. For instance, if we want to store an "identifier" for the object `user`, we can create a symbol with the name `id` for it: ```js run let user = { name: "John" }; let id = Symbol("id"); user[id] = "ID Value"; alert( user[id] ); // we can access the data using the symbol as the key ``` Now let's imagine that another script wants to have its own "id" property inside `user`, for its own purposes. That may be another JavaScript library, so the scripts are completely unaware of each other. No problem. It can create its own `Symbol("id")`. The script: ```js // ... let id = Symbol("id"); user[id] = "Their id value"; ``` There will be no conflict, because symbols are always different, even if they have the same name. Please note that if we used a string `"id"` instead of a symbol for the same purpose, then there *would* be a conflict: ```js run let user = { name: "John" }; // our script uses "id" property user.id = "ID Value"; // ...if later another script the uses "id" for its purposes... user.id = "Their id value" // boom! overwritten! it did not mean to harm the colleague, but did it! ``` ### Symbols in a literal If we want to use a symbol in an object literal, we need square brackets. Like this: ```js let id = Symbol("id"); let user = { name: "John", *!* [id]: 123 // not just "id: 123" */!* }; ``` That's because we need the value from the variable `id` as the key, not the string "id". ### Symbols are skipped by for..in Symbolic properties do not participate in `for..in` loop. For instance: ```js run let id = Symbol("id"); let user = { name: "John", age: 30, [id]: 123 }; *!* for(let key in user) alert(key); // name, age (no symbols) */!* // the direct access by the symbol works alert( "Direct: " + user[id] ); ``` That's a part of the general "hiding" concept. If another script or a library loops over our object, it won't unexpectedly access a symbolic property. In contrast, [Object.assign](mdn:js/Object/assign) copies both string and symbol properties: ```js run let id = Symbol("id"); let user = { [id]: 123 }; let clone = Object.assign({}, user); alert( clone[id] ); // 123 ``` There's no paradox here. That's by design. The idea is that when we clone an object or merge objects, we usually want *all* properties to be copied (including symbols like `id`). ````smart header="Property keys of other types are coerced to strings" We can only use strings or symbols as keys in objects. Other types are converted to strings. For instance, a number `0` becomes a string `"0"` when used as a property key: ```js run let obj = { 0: "test" // same as "0": "test" }; // both alerts access the same property (the number 0 is converted to string "0") alert( obj["0"] ); // test alert( obj[0] ); // test (same property) ``` ```` ## Global symbols As we've seen, usually all symbols are different, even if they have the same name. But sometimes we want same-named symbols to be the same entities. For instance, different parts of our application want to access symbol `"id"` meaning exactly the same property. To achieve that, there exists a *global symbol registry*. We can create symbols in it and access them later, and it guarantees that repeated accesses by the same name return exactly the same symbol. In order to create or read a symbol in the registry, use `Symbol.for(name)`. For instance: ```js run // read from the global registry let name = Symbol.for("name"); // if the symbol did not exist, it is created // read it again let nameAgain = Symbol.for("name"); // the same symbol alert( name === nameAgain ); // true ``` Symbols inside the registry are called *global symbols*. If we want an application-wide symbol, accessible everywhere in the code -- that's what they are for. ```smart header="That sounds like Ruby" In some programming languages, like Ruby, there's a single symbol per name. In JavaScript, as we can see, that's right for global symbols. ``` ### Symbol.keyFor For global symbols, not only `Symbol.for(name)` returns a symbol by name, but there's a reverse call: `Symbol.keyFor(sym)`, that does the reverse: returns a name by a global symbol. For instance: ```js run let sym = Symbol.for("name"); let sym2 = Symbol.for("id"); // get name from symbol alert( Symbol.keyFor(sym) ); // name alert( Symbol.keyFor(sym2) ); // id ``` The `Symbol.keyFor` internally uses the global symbol registry to look up the name for the symbol. So it doesn't work for non-global symbols. If the symbol is not global, it won't be able to find it and return `undefined`. For instance: ```js run alert( Symbol.keyFor(Symbol.for("name")) ); // name, global symbol alert( Symbol.keyFor(Symbol("name2")) ); // undefined, non-global symbol ``` So, for global symbols the name may be indeed helpful, as we can get a symbol by id. And for non-global symbols the name is only used for debugging purposes, like printing out a symbol. ## System symbols There exist many "system" symbols that JavaScript uses internally, and we can use them to fine-tune various aspects of our objects. They are listed in the specification in the [Well-known symbols](https://tc39.github.io/ecma262/#sec-well-known-symbols) table: - `Symbol.hasInstance` - `Symbol.isConcatSpreadable` - `Symbol.iterator` - `Symbol.toPrimitive` - ...and so on. For instance, `Symbol.toPrimitive` allows us to describe object to primitive conversion. We'll see its use very soon. Other symbols will also become familiar when we study the corresponding language features. ## Summary `Symbol` is a primitive type for unique identifiers. Symbols are created with `Symbol(name)` call. Symbols are always different values, even if they have the same name. If we want same-named symbols to be equal, then we should use the global registry: `Symbol.for(name)` returns (creates if needed) a global symbol with the given name. Multiple calls of `Symbol.for` return exactly the same symbol. Symbols have two main use cases: 1. "Hidden" object properties. If we want to add a property into an object that "belongs" to another script or a library, we can create a symbol and use it as a property key. A symbolic property does not appear in `for..in`, so it won't be occasionally listed. Also it won't be accessed directly, because another script does not have our symbol, so it will not occasionally intervene into its actions. So we can "covertly" hide something into objects that we need, but others should not see, using symbolic properties. 2. There are many system symbols used by JavaScript which are accessible as `Symbol.*`. We can use them to alter some built-in behaviors. For instance, later in the tutorial we'll use `Symbol.iterator` for [iterables](info:iterable), `Symbol.toPrimitive` to setup [object-to-primitive conversion](info:object-toprimitive) and so on. Technically, symbols are not 100% hidden. There is a built-in method [Object.getOwnPropertySymbols(obj)](mdn:js/Object/getOwnPropertySymbols) that allows us to get all symbols. Also there is a method named [Reflect.ownKeys(obj)](mdn:js/Reflect/ownKeys) that returns *all* keys of an object including symbolic ones. So they are not really hidden. But most libraries, built-in methods and syntax constructs adhere to a common agreement that they are. And the one who explicitly calls the aforementioned methods probably understands well what he's doing.