Understanding JavaScript Objects

Original author: Ben Rozenberg
  • Transfer
In this article, the author, a front-end developer, gave an overview of the main ways of creating, modifying, and comparing JavaScript objects.


Objects are one of the basic concepts in JavaScript. When I began to study them, they seemed to me quite simple: just a couple of keys and values, as described in theory.

Only after some time I began to understand that the topic was much more complicated than I thought. And then I began to study information from various sources. Some of them gave a good idea of ​​the subject, but I could not see the whole picture at once.

In this post, I tried to cover all aspects of working with objects in JS, not going too deeply into specific details, but also without losing important details that will help you understand the subject and feel more confident during its further study.

So let's start with the basics.

An object


An object in JavaScript is simply a set of properties, each of which is a key-value pair. You can refer to the keys using the dot ( obj.a ) or bracket designation ( obj ['a'] ).

Remember that brackets should be used if the key is:

  • is not a valid JavaScript identifier (there is a space in it, a dash, starts with a digit ...)
  • is variable.

One of the properties that objects in JS get when they are created is called Prototype , and this is a very important concept.

Prototype


Every object in JavaScript has an internal property called Prototype . In most browsers, you can refer to it by the symbol __proto__ .

Prototype is a way to ensure the inheritance of properties in JavaScript. So you can share the functionality without duplicating the code in memory. The method works by creating a connection between two objects.

Simply put, Prototype creates a pointer from one object to another.

Prototype chain

Every time JS searches for a property in an object and does not find it directly at the object itself, it checks the presence of the property in the prototype object. If there is no property in it, then JS will continue the search in the prototype of the associated object. This will continue until JS finds a suitable property or reaches the end of the chain.

Let's look at an example:

var cons = function () {
   this.a = 1;
   this.b = 2;
}
var obj = new cons(); </i>
 
cons.prototype.b = 3;
cons.prototype.c = 4;

cons is a constructor (just a function that can be called using the new operator ).

On the fifth line, we create a new object — a new copy of cons . Immediately after creation, obj also receives the property of the prototype.

And now we add properties ( 'b', 'c' ) to the cons object prototype .
Consider obj :

obj.a // 1 - here everything is as before , obj.a is still equal to 1.
obj.c  - obj does not have property c ! However, as previously mentioned, JS will now search for it in the prototype obj and return the value 4.

And now let's think about what the value of obj.b is and what will it be when we remove obj.b ?

Obj.b is equal to 2. We assigned the property b , but we did it for the cons prototype , so when we check obj.b , we still get 2. However, immediately after removing obj.b, JS will not be able to find b y o bj , and therefore will continue the search in the prototype and return the value 3.

Next, I want to briefly describe the various ways of creating an object and a little more about the prototypes.

Object creation


Object literal: let obj = {a: 1};
We created an object with the following prototype chain: obj ---> Object.prototype ---> null
As you can guess, object.prototype is the object's prototype, as well as the end of the prototype chain.

Object.create (): var newObj = Object.create (obj);
In newobj will next prototype chain: newobj ---> obj ---> --- Object.prototype> null

constructor. As in the example above, the constructor is just a JS function, which allows us to use the new operator to create new instances of it.

ES6 classes:

classrectangle{
  constructor(height, width) {
    this.height = height;
    this.width = width;
  } 
  getArea() {
    returnthis.height * this.width;
  }
}
let square = new rectangle(2, 2);

Square is an instance of the rectangle constructor , and therefore we can call square.getArea () // 4 , square.width , as well as all the functions inherited from object.prototype .

Which way is better? If you plan to create multiple instances, you can use ES6 or Designer. If you plan to create an object once, it is better to specify a literal, since this is the easiest way.

And now, when we learned about prototype and got acquainted with all the ways to create new objects, we can proceed to discuss one of the most confusing moments associated with objects.

Comparing and modifying objects


In JavaScript, objects are of the reference type.

When we create an object, let obj = {a: 1}; , the variable obj gets the address in the memory of the object, but not its value! It is extremely important to understand this difference, because otherwise errors can occur. When we create another object let newObj = obj , we actually create a pointer to a certain memory area obj , and not a completely new object.

This means that by executing newObj.a = 2 , we actually change obj so that obj.a becomes equal to 2!

This approach easily leads to the appearance of bugs, so many companies work with immutable objects. Instead of changing an already created object, you will have to create a new object again (a copy of the original) and make changes already in it. This is how important libraries like Redux work, and in general this is one of the main concepts of functional programming. You can read more here .

Equality

It also follows from the above that two objects can never be equal, even if they have the same properties. This is due to the fact that JS actually compares the location in the memory of objects, and two objects are never in the same memory cell.

// Two distinct objects with the same properties are not equalvar fruit = {name: 'apple'};
var fruitbear = {name: 'apple'};
fruit === fruitbear; // return false// here fruit and fruitbear are pointing to same objectvar fruit = {name: 'apple'};
var fruitbear = fruit;  
fruit === fruitbear; // return true

So, you most likely have already wondered how you can compare objects or how to perform various manipulations with objects, given the requirement for their immutability.

Consider several possibilities.

Modifying an object

Let's say it’s clear that we should not change objects in a good way, so we want to create a copy of the corresponding object and change its properties. Object.assign () comes to the rescue .

var obj = { a : 1, b : 2};
var newObj = Object.assign({}, obj,{a:2}) // {a : 2, b : 2 }

If we want to change the value of the a property of the obj object , we can use object.assign to create a copy of obj and modify it.

In the example, you can see that we first create an empty object, then copy the obj values and make our changes, eventually getting a new and ready-to-use object.

Please note that this method will not work for deep copying. Speaking of deep copying, we mean that you need to copy an object with one or more properties.

const obj = {a : 1, b : { a : 1 } };  // b property is an object

Object.assign () copies the properties of an object, so if the value of the property is a pointer to an object, only the pointer is copied.

Recursive operation is required for deep copying. Here you can write a function or simply use the _.cloneDeep method from the Lodash library .

Comparison of objects

There is one cool way to work with objects - a string conversion. In the following example, we convert both objects into strings and compare them:

JSON.stringify(obj1) === JSON.stringify(obj2) 

This approach is justified, because in the end we compare the strings, which are a pointer to a type-value. The bad news is that it does not always work, mainly because this or that order of properties of an object is not guaranteed.

Another good solution - use the method _.isEqual of Lodash , performing a deep comparison of objects.

And before you finish, let's go over some of the frequently asked questions on the subject of objects. This will help to dive deeper into the topic and apply the knowledge gained in practice.

Try to think about the solution yourself before reading the answer.

How to know the length of the object?


To get the answer, you must go through all the properties of the object one by one and count them. There are several ways to perform this iteration:

  • for in . This method covers all the countable properties of an object and a chain of its prototypes. We met with the prototype (and, I hope, learned the material), so it should be clear that the use of for in will not always be correct for obtaining the properties of the object.
  • Object.keys . This method returns an array with the keys of all its own (belonging to the specified object) counting properties. This approach is better, since we only work on the properties of the object, not referring to the properties of the prototype . However, there are situations when you set the enumerable attribute of a property to false, and object.keys eventually misses it, and you get an incorrect result. This happens rarely, but in such cases, getOwnPropertyNames will be very useful .
  • getOwnPropertyNames returns an array containing all the object's own keys (both countable and uncountable).

Also worth mentioning are:

  • Object.values ​​enumerates its own countable properties and returns an array with the appropriate values .
  • Object.entries enumerates its own countable properties and returns an array with keys and their values .

I think you noticed that most of the above methods return an array. This is an opportunity to take full advantage of JavaScript methods for working with arrays.

One of these methods is array.length . As a result, we can just write

let objLength = Object.getOwnPropertyNames(obj).length;

How to check if an object is empty?


  1. JSON.stringify (myObj) === “{}”  . Here, we again use the string conversion tool to easily check if the object is empty (comparing strings, not objects).
  2. ! Object.keys (myobj) .length // true  . As I mentioned, converting object keys to an array can be very useful. Here we use the convenient length property inherited from Array.prototype , checking with it the length of the keys in the array. In JS 0 it turns into false, therefore adding ! we make it true. Any other numbers will turn false.

Finally


I hope, now you feel more confident in creating objects and working with them. Let's summarize:

  • Remember that objects belong to the reference type, which means that it is recommended to work with them without changing the original objects.
  • Make friends with the prototype property and the prototype chain.
  • Get acquainted with the tools, assistants in working with objects. Remember that you can turn objects into strings, get an array with their keys, or simply iterate over their properties using a set of methods that we met.

Good luck learning JavaScript objects.  

image

Also popular now: