Object property keys may be string or symbol type A “symbol” represents a unique identifier Symbols are guaranteed to be unique Even if we create many symbols with the same description, they are different values Create let id1 = Symbol("id") let id2 = Symbol("id") id1 == id2 // false typeof id1 // "symbol" alert(id1) // TypeError: Cannot convert a Symbol value to a string alert(id1.toString()); // "Symbol(id)" Description property let id1 = Symbol("id") id1.description // "id" Symbol in object literal let id = Symbol("id"); let user = { name: "John", [id]: 123 // not "id": 123 // That’s because we need the value from the variable id as the key, not the string “id” }; “Hidden” properties we can access the data using the symbol as the key nobody can overwrite it, coz nobody can generate same id, symbol cannot be accessed accidentally let user = { name: "John" }; let id = Symbol("id"); user[id] = 1; user[id] // 1 Symbols are skipped by for…in loop for (let key in user ) { console.log(key, user [key]); // name John } // Symbol(id): 1 is skipped Object.keys(user) // ["name"] // Symbol is not shown Copy object with Symbol let id = Symbol("id"); let user = { [id]: 123 }; let clone = Object.assign({}, user); clone[id]; // 123 Global symbols We can create symbols global registry & access them later It guarantees that repeated accesses by the same name return exactly the same symbol. Symbol.for(key) checks the global registry, and creates or returns existing symbol Symbols inside the registry are called global symbols They are accessible everywhere in the code – that’s what they are for. There are many system symbols used by JavaScript which are accessible as Symbol.* We can use them to alter some built-in behaviors // read from the global registry let id = Symbol.for("id"); // if the symbol did not exist, it is created // read it again (maybe from another part of the code) let idAgain = Symbol.for("id"); id === idAgain // true // the same symbol // Symbol.for works only for global symbols let globalSymbol = Symbol.for("name"); let localSymbol = Symbol("name"); Symbol.keyFor(globalSymbol) // name, global symbol Symbol.keyFor(localSymbol) // undefined, not global globalSymbol.description // name localSymbol.description // name // There are many system symbols used by JavaScript which are accessible as Symbol.*. // We can use them to alter some built-in behaviors. Symbol.hasInstance Symbol.isConcatSpreadable Symbol.iterator Symbol.toPrimitive // …and so on. Example to understand symbols let lib = { name: "ABC" }; lib["id"] = 5; lib["id"] = 6; // The value is changed because it is String [KEY]!! lib[Symbol("id")] = 123; lib[Symbol("id")] = 124; //Not changed lib // { name: "ABC", id: 6, Symbol(id): 123, Symbol(id): 124 } Object to primitive conversion For data conversion into a primitive value JavaScript tries to find and call three object methods: Call obj[Symbol.toPrimitive](hint) – the method with the symbolic key Symbol.toPrimitive . Otherwise if hint is string JS tries obj.toString() or obj.valueOf() . Otherwise if hint is number or "default" JS tries obj.valueOf() or obj.toString() . Knowing that we can make our data be convertible into primitive value by JS natively. Basic object is not converted into number or boolean. let obj = {a: "5"} Boolean({obj}) // true // All objects are true in a boolean context obj.toString() // "[object Object]" +obj // NaN But when we add symbol it can do conversion. let user = { name: "John", money: 1000, [Symbol.toPrimitive](hint) { return hint == "string" ? this.name : this.money; } }; alert(user); // "John" alert(+user); // 1000 alert(user + 500); // 1500 In the absence of Symbol.toPrimitive conversion can be handled by valueOf() , toString() . let user = { name: "John", money: 1000, // for hint="string" toString() { return this.name; }, // for hint="number" or "default" valueOf() { return this.money; } }; alert(user); // "John" alert(+user); // 1000 alert(user + 500); // 1500 Make iterable contactable let arr = [1, 2] let arrayLike = { 0: "something", length: 1, } arr.concat(arrayLike) // [1,2,[object]] let arr = [1, 2]; let arrayLike = { 0: "something", 1: "else", [Symbol.isConcatSpreadable]: true, length: 2, }; arr.concat(arrayLike) //[1, 2, "something", "else"] Make object iterable To make an iterable we need to add Symbol.iterator & next() method. let range = { from: 1, to: 5, [Symbol.iterator]() { this.current = this.from; return this; }, next() { if (this.current <= this.to) { return { done: false, value: this.current++ }; } else { return { done: true }; } } }; for (let num of range) alert(num); // 1, then 2, 3, 4, 5