📜 ⬆️ ⬇️

Details about javascript objects

The author of the material, the translation of which we are publishing today, says that there are many things in JavaScript objects that one could not even suspect about the existence of using them in their daily work. Objects in JavaScript are very easy to create, it is convenient to work with them, they seem to be understandable and flexible entities, and many programmers simply do not think that objects are actually arranged is not so simple.


NB: Information from the publication in practice should be applied very carefully and under the supervision of more experienced colleagues.

Here we will talk about what is hidden in the depths of objects and discuss the subtleties of working with them.
Having mastered this material, you will know the answers to the following questions:


 obj.id = 5; console.log(obj.id) // => '101' (5 в двоичной системе счисления) 

Types of properties of objects


▍Properties that store data


You have probably created countless objects resembling this one:

 const obj = { name: 'Arfat', id: 5 } obj.name // => 'Arfat' 

The name and id properties of the obj object are called data obj properties, or “data properties” (Data Properties). These are familiar properties that are constantly found in JavaScript code. What other kinds of properties can objects have?

▍Properties with access methods


Such properties are also known as getters and setters, they are found in other programming languages ​​like C # or Python. A property with access methods (Accessor Property) is a combination of two functions, get and set .

When declaring such properties instead of using the traditional construction of the form ключ: значение , the following syntax is used:

 const accessorObj = { get name() {   return 'Arfat'; } }; accessorObj.name; // => 'Arfat' const dataObj = { name: 'Arfat', }; dataObj.name; // => 'Arfat' 

Take a look at the accesorObj object and compare it with the dataObj object. As you can see, they are now showing the same behavior. In describing the first object, we used the get keyword, followed by the function declaration. In order to access such a property, although it is represented by a function, it is not necessary to put parentheses after the name of the property to call this function. That is a construction like accessorObj.name(); is incorrect.

When you try to access the accessorObj.name property, that is, when you try to read it, the corresponding function is executed and the value returned to it becomes the value of the name property.

The get functions are called getters, they are responsible for getting values. If we continue our example and try to change the value of the name property of the accessorObj object, say, by running the command accessorObj.name = 'New Person'; then it turns out that nothing will happen. The point here is that the setter function is not associated with the name key. Such functions allow you to customize the order in which new values ​​are assigned to properties of objects, access to which is organized using getters.

Here’s what an object declaration with a getter and setter looks like:

 const accessorObj = { _name: 'Arfat', get name() {   return this._name; }, set name(value) {   this._name = value; } }; 

The setter function gets what it is trying to assign to the property of the object, as a parameter. Now you can save something in the object property. In this case, we create a “private” property of the _name object. The first symbol of the name of such a property is an underscore, which is nothing more than a hint for the programmer, indicating that this property is intended for the internal needs of the object. Next, we work with it when accessing the property of the name object, access to which is regulated by a getter and a setter.

In the getter function, before returning the value of the _name property, we can modify it.

Here is what it might look like:

 const obj = { get name() {   return this._name.toUpperCase(); }, set name(value) {   this._name = value; }, get id() {   return this._id.toString(2); // Преобразуем десятичное число в его двоичное представление }, set id(value) {   this._id = value; } } obj.name = 'Arfat'; obj.name; // => 'ARFAT' obj.id = 5; obj.id; // => '101 

This program, by the way, contains an answer to one of the questions given at the beginning of the article, which concerns the analysis of a code that is incomprehensible at first glance.

Why would anyone need properties with access methods if you can safely work with ordinary properties? For example, they may be needed in order to log information about property read operations, or to keep a history of changes in property values. Properties with access methods give us all the possibilities of data processing with the help of functions and the simplicity characteristic of working with ordinary properties. More information about the use of such properties can be found here .

How does JavaScript distinguish regular properties that store data from properties with accessors? Let's figure it out.

Object Property Descriptors


At first glance it may seem that there is a direct correspondence between the keys and the values ​​stored in the objects. However, this is not quite true.

Свойств Property Attributes


Associated with each object key is a set of attributes that define the characteristics of the value associated with a given key. These attributes can also be viewed as metadata describing a ключ: значение pair.

Attributes are used to set and describe the state of object properties. The set of property attributes is called a descriptor. There are six attributes of properties:


Why are the property attribute names in this list enclosed in [[]] ? Double parentheses indicate that these are entities used by the internal mechanisms of the language. The JS programmer cannot access these properties directly. In order to influence them, appropriate methods are used.

Consider the following image taken from here , where you can see the object and the attributes of its properties.


Object and attributes of its properties

Our object has 2 keys - x and y . In addition, each of them is associated with a set of attributes.

How can JavaScript get object information similar to those shown in the previous figure? You can use the Object.getOwnPropertyDescriptor() function to do this. It takes an object and the name of its property, and then returns an object containing the attributes of this property. Here is an example:

 const object = { x: 5, y: 6 }; Object.getOwnPropertyDescriptor(object, 'x'); /* { value: 5, writable: true, enumerable: true, configurable: true } */ 

It should be noted that the composition of the attributes of a particular property depends on its type. All six attributes of the same property are not found.


▍ [[Value]]


This attribute stores what is issued when trying to get the value of an object property. That is, if in the previous example we use a construct of the object.x type, we get what is stored in the [[Value]] attribute. The same thing will happen when trying to read the properties of an object using square brackets.

▍ [[Get]]


This attribute stores a reference to a function that is a getter property. This function is called without arguments when trying to read the value of a property.

▍ [[Set]]


Here is a reference to the function declared when creating a setter property. It is called with an argument representing the value that the property was attempted to assign, that is, it is called with each assignment operation to the property of a new value.

 const obj = { set x(val) {   console.log(val)   // => 23 } } obj.x = 23; 

In this example, the right-hand side of the expression is passed as the argument val to the setter function. Here is the code that demonstrates the use of setters and getters.

▍ [[Writable]]


This attribute stores a boolean value. It indicates whether the value of the property can be rewritten or not. If false is stored here, then attempts to change the value of the property will not succeed.

▍ [[Enumerable]]


Logical value is also stored here. This attribute governs the output of a property in for-in loops. If it is set to true , then the property will be able to work using such cycles.

▍ [[Configurable]]


This attribute is also represented by a logical value. This is what happens if it holds false :


The effect of setting this attribute to false also depends on the type of property. This attribute, in addition to the above effects on the properties, acts on them and so:


Work with descriptors


Now that we are familiar with the attributes, we will ask ourselves how we can influence them. In JavaScript, there are special functions designed to work with property descriptors. Let's talk about them.

Object Method Object.getOwnPropertyDescriptor ()


We have already met with this method. He, accepting object and a name of its property, returns either undefined , or object with a property descriptor.

Object Method Object.defineProperty ()


This is a static Object method that allows you to add properties to objects or change existing properties. It takes three arguments — an object, a property name, and an object with a handle. This method returns a modified object. Consider an example:

 const obj = {}; // #1 Object.defineProperty(obj, 'id', { value: 42 }); // #2 console.log(obj); // => { } // #3 console.log(obj.id); // => 42 // #4 Object.defineProperty(obj, 'name', { value: 'Arfat', writable: false, enumerable: true, configurable: true }); // #5 console.log(obj.name); // => 'Arfat' // #6 obj.name = 'Arfat Salman' // #7 console.log(obj.name); // => 'Arfat' // (а не 'Arfat Salman') Object.defineProperty(obj, 'lastName', { value: 'Salman', enumerable: false, }); console.log(Object.keys(obj)); // => [ 'name' ] // #8 delete obj.id; // #9 console.log(obj.id); // => 42 //#10 Object.defineProperties(obj, { property1: {   value: 42,   writable: true }, property2: {} }); console.log(obj.property1) // => 42 

It can be run in the Node.js environment. The code is quite large, but, in fact, it is quite simple. Let us analyze it, focusing on the comments of the form // #n .

In fragment #1 we use the defineProperty function, passing it an obj object, the name of the id property and a descriptor object that contains only the value property, indicating that the value [ 42 [[Value]] will be written to the [[Value]] attribute. Remember that if you do not pass values ​​for attributes like [[Enumerable]] or [[Configurable]] in this object, they will be set to false by default. In this case, the [[Writable]] , [[Enumerable]] and [[Configurable]] attributes of id set to false .

In the place marked as #2 , we are trying to output a string representation of the object to the console. Since its id property is not enumerable, it will not be displayed. At the same time, the property exists, which proves its successful output by command #3 .

Creating an object (fragment #4 ), we set the complete list of attributes. In particular, we set [[Writable]] to false .

With commands #5 and #7 we display the value of the name property. But between them (fragment #6 ) we tried to change this value. This operation did not change the value of a property, because its [[Writable]] attribute is set to false . As a result, both commands output the same to the console.

Command #8 is an attempt to delete the id property. Recall that its [[Configurable]] attribute is set to false , which means that you cannot delete it. This is proved by team #9 .

Slice #10 shows the use of the Object.defineProperties () function. It works in the same way as the defineProperty() function, but allows, in one call, to affect several properties of an object, while defineProperty() works with only one property of an object.

Protection of objects


Periodically, the developer needs to protect objects from outside interference. For example, given the flexibility of JavaScript, it is very easy to mistakenly change the properties of an object, which should not change. There are three main ways to protect objects.

Object Method Object.preventExtensions ()


The Object.preventExtensions() method prevents the object from expanding, that is, adding new properties to it. It takes an object and makes it non-expandable. Note that you can delete properties from such an object. Consider an example:

 const obj = { id: 42 }; Object.preventExtensions(obj); obj.name = 'Arfat'; console.log(obj); // => { id: 42 } 

To find out if an object is non-extensible, you can use the Object.isExtensible() method. If it returns true , then new properties can be added to the object.

Object Method Object.seal ()


The seal() method “seals” objects. This is what it is about:


As a result, it turns out that this method prevents the addition of new properties to the object and the deletion of existing properties in it.

Consider an example:

 const obj = { id: 42 }; Object.seal(obj); delete obj.id // (не работает) obj.name = 'Arfat'; // (не работает) console.log(obj); // => { id: 42 } Object.isExtensible(obj); // => false Object.isSealed(obj); //=> true 

To check whether an object is “sealed” or not, you can use the Object.isSealed() method.

Object Method Object.freeze ()


The freeze() method allows you to “freeze” objects, equipping them with the maximum possible JavaScript level of protection. Here is how it works:


Here is an example:

 const obj = { id: 42 }; Object.freeze(obj); delete obj.id // (не работает) obj.name = 'Arfat'; // (не работает) console.log(obj); // => { id: 42 } Object.isExtensible(obj); // => false Object.isSealed(obj); //=> true Object.isFrozen(obj); // => true 

You can check whether an object is “frozen” by using the Object.isFrozen() method.

▍ Overview of methods used to protect objects


It is important to note that the methods described above, used to protect objects, affect only their properties, which are not objects.

Here is a summary table on the considered methods of protection of objects, which is taken from here .
Property creation
Reading property
Property rewrite
Property removal
Object.freeze()
-+
--
Object.seal()
-+
+
-
Object.preventExtensions()
-+
+
+

Results


Considering how often objects are used in JavaScript code, it is important for each developer to know how they work. We hope that what you have learned by reading this material will be useful to you. In addition, now you know the answers to the questions listed at the beginning of the article.

Dear readers! How do you protect JavaScript objects?

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