© Russ Ferguson and Keith Cirkel 2017
Russ Ferguson and Keith CirkelJavaScript Recipes10.1007/978-1-4302-6107-0_15

15. Working with Symbols

Russ Ferguson and Keith Cirkel2
(1)
Ocean, New Jersey, USA
(2)
London, UK
 

What Are the Advantages of Using Symbols ?

Problem

You want to know why and how to use symbols in a project.

Solution

Symbols give you the ability to create unique tokens that will never clash with other symbols. Similar to UUIDs (Universal Unique Identifiers) , symbols can be used on objects to make unique properties.

The Code

let mySym1 = Symbol('This is my first Symbol');
let mySym2 = Symbol(); //returns false
console.log(mySym1 == mySym2);
console.log(mySym1.toString()) //returns  Symbol(This is my first Symbol)
Listing 15-1.
Creating Symbols

How It Works

Symbols are unique objects that can be used as properties of an object. They are primitive datatypes, similar to Number or String. When creating a symbol, the syntax is similar to creating an object instance. The main difference is that you do this without the new operator. Using new when creating an object would result in a TypeError.
When creating a symbol, you can also create a label for that symbol. A label does not change the value of the symbol; it is mostly for debugging. To create a label, pass a string as the first parameter of the symbol constructor. If you wanted to see the label, for example in the browser’s console , use the toString method. Symbols with the same label are not equal to each other.

How Do You Create a Symbol?

Problem

You want to create a symbol to use as an object property.

Solution

Creating symbols is very similar to creating an object instance. One exception is you should not use the new operator. This would throw an TypeError. Each call of the Symbol function generates a unique value. Labels can be added to symbols for debugging purposes. These labels do not change the value generated by a symbol. Using bracket notation, you can use symbols as properties of an object, thereby creating unique values for each property.

The Code

flet characterObj = {};
let dad = Symbol();
characterObj.name ='Elliot'
characterObj[dad] = 'Mr. Robot';
console.log(Object.keys(characterObj));
Listing 15-2.
Creating Symbols to Use as Object Properties

How It Works

Once a symbol has been created, you can use bracket notation to make it the property of an object. Because you are using a symbol, the property has a unique value that cannot clash with another symbol. The symbol can then be given a value. The unique values of symbols make them different from using strings or numbers as property keys.

Can You Access a Symbol by its String Name?

Problem

The symbol global registry contains many symbols. How do you access the one you want?

Solution

Using the Symbol.for() method will allow you to access a symbol from the registry. If the symbol does not exist, one will be created.

The Code

let player1 = Symbol.for('player1'); //creates the symbol and puts it in the registry
console.log(Symbol.for('player1') == player1); //returns true
Listing 15-3.
Accessing a Symbol Using the Symbol.for() Method

How It Works

The global symbol registry constrains multiple symbols. It is very similar to having multiple global objects. Using the Symbol.for() method allows you to retrieve a symbol if it currently exists. If the symbol is not currently in the registry, the symbol will be created. Moving forward every time there is a request for that symbol the same one will be returned. Symbols in the registry can be accessed from different realms. For example, Iframes and service workers can share symbols.

Can You Return a Key Based on a Symbol?

Problem

You want to return a key based on a symbol.

Solution

The Symbol.keyFor() method returns a key based on its symbol.

The Code

let firstPlayer = Symbol.for('player1'); //creates the symbol and puts it in the registry
console.log(Symbol.keyFor(firstPlayer)) //returns player1
Listing 15-4.
Using the forKey() Method You Can Return the Key of a Symbol

How It Works

When using the keyFor() method, it will return the key for any given symbol that is in the registry. If this key does not exist, the method will return undefined.

Can You Use a Symbol as Part of a Function or Method in an Object?

Problem

You want to know if symbols can be used to define functions.

Solution

Because of computed property names , symbols can be used as part of a function definition.

The Code

let helloSymbol = Symbol('Hello World Function');
let myObj = {
        [helloSymbol] () {
               return 'Hello World';
        }
}
console.log(myObj[helloSymbol]());
let iterableObj = {
        Symbol.iterator]() {
            let dataArray = ['this', 'that', 'other'];
            let currentIndex = 0;
                return {
                     next(){
                          if(currentIndex < dataArray.length){
                                return {value: dataArray[currentIndex++]};
                                       }else{
                                            return {done: true};
                                      }
                             }
                     }
                }
       }
for(let x of iterableObj){
      console.log(x); //returns this, that, other
}
Listing 15-5.
Computed Property Names Enable You to Use Symbols as Part of a Function Definition

How It Works

Computed property names is a ES6 feature that allows you to create a property for an object literal or class using bracket ([ ]) notation. By first creating a public symbol, then using it as a property, this property will always be unique.
Another example is if the symbol was an iterator. This would make the object iteratable. In the second example the object has properties stored in an array. Using Chapter 13 as a reference on iterators, we can use the next function to continue to move to the next property until currentIndex has the same value as the length of the array. Then use a for-of loop to iterate through the object.

How Can a Symbol Override the Constructor Function of Your Custom Class ?

Problem

You have a situation where you want to return the constructor from a derived objet and not the current class.

Solution

The Symbol.species property will return the constructor function of the main class and not a subclass.

The Code

class ArraySubClass extends Array{
        static get [Symbol.species]() {return Array;}
}
var subClassInstance = new ArraySubClass(1,2,3,4,5,6);
var derivedObj = subClassInstance.filter(function(value){
    if(value % 2){
    return value
    }
});
console.log(derivedObj) //returns 1,3,5
console.log(derivedObj instanceof Array); //returns true
console.log(derivedObj instanceof ArraySubClass); //returns false
Listing 15-6.
The Property Species Used on the Symbol Object Returns the Constructor of a Derived Object

How It Works

To fully understand what Symbol.species does, you must first understand what a derived object is.
Derived objects are created when an operation is called on the original object. In our example, ArraySubClass extends Array. Because of this ArraySubClass on its own it does not have a filter() method. By calling the filter() method, the original Array object is used to fulfill the request.
The object that returns as the result of the filter has a constructor of the parent Array object and not that of the ArraySubClass. Using the instanceof operator shows that the derived object does not have the same constructor of the ArraySubClass but that of the Array object.

Can a Constructor Object Recognize an Object as its Instance?

Problem

You need to know if an object is an instance of a certain type.

Solution

Creating a custom instanceof operator with the hasInstance property will allow you to check for type .

The Code

class CheckArrayInstance {
      static [Symbol.hasInstance](instance){
             return Array.isArray(instance)
      }
}
var myArray = new Array();
console.log(myArray instanceof CheckArrayInstance); //returns true
Listing 15-7.
The hasInstance Property Will Check if the Constructor Object Recognizes Another Object as Instance

How It Works

Usually when using the instanceof operator , you can check the prototype of an object. However, you can customize the instanceof operator to get more specific and check the type of an object.
In Listing 15-7, we create the static method that checks if the instance is an array. If true, the method returns a value of true.

Can You Convert an Object to a Primitive Value ?

Problem

You want to determine the primitive value of an object.

Solution

When used as a method of an object, Symbol.toPrimitive can convert an object to its primitive type.

The Code

var  PrimitiveObj = {
           [Symbol.toPrimitive](hint){
     if(hint == 'number'){
             return 100 ;
                        }else if (hint == 'string'){
                                 return 'this is a string';
                            }else{
                                 return 'this is the default;
                            }
               }
     }
     console.log(+PrimitiveObj) //returns 100
     console.log(`${PrimitiveObj}`) //returns this is a string
     console.log(PrimitiveObj + ' ')  //returns this is the default
Listing 15-8.
The toPrimitive Method Converts an Object to its Primitive Type

How It Works

The toPrimitive method lets you create a function that will turn an object into a primitive. This method takes one property called hint . This property can have only one of three values—string, number, or default. In Listing 15-8, we call the function three different times.
The first time the plus (+) operator is used. Using this operator performs type conversion . Using this operator, the object will be converted into a number. Under normal circumstances, the result would be not a number (NaN). Using the toPrimitive function allows us to see that the conversion to number was trying to take place. The result is that hint then has the value of number.
The second example uses a template literal (discussed in detail in Chapter 14). Since template literals use string literals, the value of hint would be string.
The third call would by default return an object when making the conversion. Because of our toPrimitive method, it returns default.

How Can You Get the Class of the Object You Are Working With?

Problem

You want to know the type of object you are working with and not have [object object] as a return value.

Solution

Adding the toStringTag property to a custom class will allow you to return the type of object you are working with.

The Code

var myDate = new Date();
var myArray = new Array();
var myObj = new Object();
class myClass{};
var myClassObj = new myClass();
console.log(Object.prototype.toString.call(myDate));  //returns [object Date]
console.log(Object.prototype.toString.call(myArray)); // returns [object Array]
console.log(Object.prototype.toString.call(myObj));  //returns [object object]
console.log(Object.prototype.toString.call(myClassObj));  //returns [object object]
class WithToStringTag{
      get [Symbol.toStringTag](){
              return 'WithToStringTag';
      }
}
var withToStringTagObj = new WithToStringTag();
console.log(Object.prototype.toString.call(withToStringTagObj)); //returns [object WithToStringTag]
Listing 15-9.
The toStringTag Property Used in a Class Will Return the Type

How It Works

The built-in toString() method can be used in every object. This will allow you to get the class the object is based on. In order to access this method, we can use Object.prototype.toString.call. Using the call() method is important. Here it sets the value of this and has the result comes from the object that we are passing. For further understanding of the call() method , look at Chapter 12.
In Listing 15-9, all of the objects that are part of the JavaScript language return their class names. When you reach custom objects, the result is [object object]. By adding the Symbol.toStringTag method, you can return the name of the custom class and override the built-in toString method.

Can You Hide Properties of an Object?

Problem

You want certain properties not to show up when you’re doing a with statement.

Solution

The well-known Symbol.unscopables symbol will exclude property names from being exposed using a with statement.

The Code

class MyClass{
      firstProp() {return 'First Prop';}
}
with(MyClass.prototype){
      console.log(firstProp()) //returns First Prop
}
class MyClassWithUnscopables{
      firstProp(){return 'First Prop';}
      get[Symbol.unscopables](){
               return {firstProp : true}
      }
}
with(MyClassWithUnscopables.prototype){
       console.log(firstProp()); //ReferenceError: firstProp is not defined
}
Listing 15-10.
Symbol.unscopables Will Prevent Properties from Being Exposed Using a with Statement

How It Works

The Mozilla Developer Network (MDN) does not recommend using the with statement. However, when an expression is being used inside a with statement, methods could be called on that object. This brings us to Symbol.unscopables , which enables you to prevent certain properties from being exposed to the scope from inside the with statement.
Adding the unscopables symbol to the custom class protects the method from being executed within the with statement. In this instance, it returns a ReferenceError because it does not believe the method exists.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.191.139.169