📜 ⬆️ ⬇️

Exploring JavaScript Symbols. Symbol - new data type in javascript

This is the first part about characters and their use in javascript.

The new ECMAScript (ES6) specification introduces an additional data type, the symbol. It will add to the list of already available primitive types (string, number, boolean, null, undefined). An interesting peculiarity of a symbol compared to other primitive types is that it is the only type that does not have a literal.

What was the need for an additional data type?
')
In JavaScript, it is not possible to declare an object property as private. To hide the data, you can use closures, but then all the properties need to be declared in the constructor (since there is no possibility to declare them in the prototype), moreover, they will be created for each instance, which will increase the size of the memory used. ECMAScript 5 provided the ability to specify enumerable: false for a property, which allows you to hide a property from being listed in for-in and will not be visible in Object.keys , but for this you need to declare it via the Object.defineProperty construction.

  var user = {}; Object.defineProperty( user, 'role', { enumerable: false, value: 'admin' }); 

This construction of the ad still does not preclude the possibility of obtaining the value of the property, if you directly refer to it:

  var userRole = user.role; // 'admin' 

In other languages, for example, you can add a method modifier to determine its visibility (protected, private, public). But in the new JavaScript specification, they chose a different approach and decided not to introduce modifiers, but to determine the behavior depending on the type of the property identifier. Previously, the property name was a string, but now it can be either a string or a character. This approach allows us not to change the very concept of declaring objects:

  var role = Symbol(); var user = { id: 1001, name: 'Administrator', [role]: 'admin' }; 

In this example, a user object is declared, in which two properties are declared via string identifiers ( id , name ) and one property via a symbol ( role ).
The role property is declared in square brackets so that it is not interpreted as a string, but was obtained by evaluating the expression. This object can also be declared as follows in order to better understand this construction:

  var role = Symbol(); var user = { ['id']: 1001, ['name']: 'Administrator', [role]: 'admin' }; 

In this case, all three expressions will be evaluated and their results will be the names of the properties. The ability to use dynamic (derived by evaluating an expression) property names for object literals is added to ES6.

The key feature of the symbol, in which it differs from the string, is that it is possible to access a property that is declared through a symbol only by reference to this symbol. For example, if the user object needs to get the user name, you need to write this code:

  var userName = user.name; // 'Administrator' // OR var userName = user['name']; // 'Administrator' 

We cannot get the user role in this way:

  var userRole = user.role; // undefined // OR var userRole = user['role']; // undefined 

In order to get a role, you need to access the property by reference to the symbol:

  var role = Symbol(); var user = { id: 1001, name: 'Administrator', [role]: 'admin' }; var userRole = user[role]; // 'admin' 

The property declared through the symbol will not be visible in for-in , Object.keys , Object.getOwnPropertyNames , and will not be added when using JSON.stringify .

Consider the features of characters.

As already shown in the example above, to create a symbol, call the Symbol function:

  var score = Symbol(); 

The Symbol function also takes an optional parameter — a string that serves to describe a symbol:

  var score = Symbol('user score'); console.log( score ); // Symbol(user score) 

A symbol description serves only to help with debugging, it does not change the behavior of a symbol and you cannot access the symbol through the description, there is also no method to get or change the symbol description.

The ES6 specification no longer supports the explicit creation of primitive objects, so the following construct will throw an error:

  var score = new Symbol('score'); // TypeError 

For backward compatibility for String , Number and Boolean , the error will not be thrown away (but it is better not to use the default behavior). If you want to work not with the primitive, but with its object, you can use the Object function passing it to the primitive as a parameter:

  var symbol = Symbol('symbol'); var string = 'string'; var number = 5; var symbolObj = Object( symbol ); var stringObj = Object( string ); var numberObj = Object( number ); console.log( symbol ); // Symbol(symbol) console.log( string ); // 'string' console.log( number ); // 5 console.log( symbolObj ); // Symbol {} console.log( stringObj ); // String { 0: 's', 1: 't', 2: 'r', 3: 'i', 4: 'n', 5: 'g', length: 6, [[PrimitiveValue]]: 'string' } console.log( numberObj ); // Number { [[PrimitiveValue]]: 5 } 

An important feature of the symbol is also that its value is unique:

  var firstScore = Symbol('score'); var secondScore = Symbol('score'); firstScore === secondScore; // false 

This behavior opens up more possibilities for us when working with objects, for example, several modules can extend an object with new properties without worrying about possible name conflicts.

Typeof can be used to define a symbol, in case the value is a symbol, the string symbol is returned:

  function isSymbol( value ) { return typeof value === 'symbol'; } var firstScore = Symbol('score'); var secondScore = 'score'; isSymbol( firstScore ); // true isSymbol( secondScore ); // false 

In the current JavaScript type casting system, there are many nuances and characters add another feature to the fact that, unlike other primitive values, a character cannot be converted to a string or number. If you try to convert to a number or string, a TypeError error will be thrown. This behavior is chosen in order not to accidentally create a string value, which will eventually be used as the property name:

  var userObject = {}; var role = Symbol() + 'type'; var id = 10001; userObject.id = id; userObject[ role ] = 'admin'; 

In this example, it is not clear what the result should be saved to the role variable, if the string is, then the userObject[ role ] = 'admin' property will be declared through the string and it will be directly accessed (but because the symbol was used, most likely there was a desire hide property value). On the other hand, if the expression results in a symbol, and since you cannot get the values ​​of a symbol, it means you cannot determine if the type string is in it, and this is no longer an obvious behavior and you need to inform the developer in situations when he intentionally tries to create a string value from the symbol , because such a construction does not make sense.

To avoid such ambiguity, and the behavior was chosen that when you try to convert the character will be an error.

This is basic information about characters, like a data type. In the next part, we will continue to look at the symbol and study the methods of the symbol (how to create a global symbol, how Object.getOwnPropertySymbols works), and also look at possible examples of using the symbol.

Source: https://habr.com/ru/post/255137/


All Articles