Experimenting with Javascript Objects (Part 1)

Davide Ceschia
7 min readFeb 21, 2022

--

Introduction to Javascript Objects

Photo by Michal Matlon on Unsplash

Javascript is a very complicated yet powerful language and objects are in my opinion one of the hardest parts of it, I decided to write this series of articles to help others get a more in-depth understanding and use cases of objects. This first article is just an introduction to objects, in the next ones we will start exploring various practical applications.

What is a Javascript Object?

A Javascript object is a data type used to store keyed collections, you can create an object using the Object() constructor or the object literal syntax. There are actually a lot more ways you can create an object, but I’ll just show some of them.

90% of the time what you’ll be using is the object literal syntax, it’s for sure the easiest and cleanest solution. The object constructor is rarely used and you should generally not worry about it. The object constructor function is a bit more useful but still only used in special situations.

Working with Objects

Defining/Mutating Properties

There are multiple ways to define properties in an object. You can

  • Define them using an object literal during initialization
  • Use the assignment operator
  • Use Object.defineProperty

This is a bit more advanced, but it is very very useful. It allows you to define properties with specific options, for example, you can make the property non-enumerable or non-mutable. It returns the resulting object and throws an error on failure.

There is a lot going on here, let’s break it down.
By calling defineProperty with all options set to true is functionally equivalent to the property assignment operator myObject.hi = "mom" .

Things change when you switch the options. Settings writable to false prevents anyone from mutating the property using the assignment operator, setting configurable to false prevents the use of defineProperty on the same property again and enumerable decides whether the property is enumerable or not. We will see how to loop over enumerable and non-enumerable properties shortly.

As you can see from the results below, calling defineProperty on a property that was set to not be configurable will result in a TypeError .

  • Reflect.defineProperty

This is very similar to Object.defineProperty but instead of returning the object, it returns a boolean with the result of the operation. If the property was already defined as non-configurable, instead of throwing an error it will return false.

Accessing Properties

Accessing properties is very similar to defining them, you just don’t have to use the assignment operator.

To access all properties in an object there are a lot of ways to do that, you could

  • Use for…in
    for…in will loop over all enumerable properties, even those contained in the prototype chain. Non-enumerable properties will not be listed.

results:

> hi mom
> hello world

myObject.hasOwnProperty is required to avoid accessing properties in the prototype chain, we’ll understand that in a moment.

  • Use Object.keys
    Object.keys will only loop over enumerable properties contained in the object itself, it will not traverse the prototype chain.

You could also use Object.values or Object.entries .

  • Use Object.getOwnPropertyNames
    Object.getOwnPropertyNames will loop over all enumerable and non-enumerable properties in the object itself, it will not check the prototype chain.

Deleting Properties

Deleting properties is pretty straightforward, but first, we need to make it clear that deleting a property IS NOT the same as setting the property to null/undefined. If you set a property to undefined, iterators will still loop over it and Object.hasOwnProperty will still return true.

  • Use the delete keyword

The first delete works because by default property assignments are configurable, but the second delete operation fails silently because the property could not be deleted. That is where Reflect.deleteProperty comes.

  • Use Reflect.deleteProperty

Reflect.deleteProperty is functionally the same as the delete keyword but it’s a function and it returns the result of the delete operation.

As you can see we can now check the result of the operation and act accordingly.

Const vs Let vs Var

All three keywords are used to declare a variable, but what’s the difference?

Before ES6, var was the de-facto (and only) way to declare variables, nowadays it’s a way better practice to use let and const . I won’t go too much into the details about the differences (I might write a dedicated article about it) but the point is that you should never use var. You should always use const unless you need to reassign the variable, in which case you should use let .

What I wanted to emphasize is that const prevents reassignments but not mutations to objects! You should still use it if you want the variable to not be reassigned but keep in mind that properties can generally still be mutated/added/deleted (unless explicitly specified using Object.defineProperty or Reflect.defineProperty ).

Javascript Objects are references

In javascript only Number, String, and Boolean types are passed around by value, everything else is passed by reference . That means that the value is not copied, but a reference to the original value is being passed instead, resulting in mutations not being limited to the function context. Let’s see an example.

As you can see from the results, the original object is being mutated, this is a very important concept when understanding objects.

Functions are Objects

As we saw in the beginning, you can create an object using the Object constructor function syntax, but all JS functions are just Objects. The only difference between functions and objects is that functions can be called, objects cannot.

Arrays are Objects

It might be weird but in Javascript, arrays are objects as well. You can define properties on an array. All the functions and methods we talked about in the Working with Objects work on arrays as well.

results:

> [1, 2, 3, hello: 'world', hi: 'mom']
> key: 0
> key: 1
> key: 2
> key: hello
> value: 1
> value: 2
> value: 3

As you can see we can easily set properties with the assignment operator or with Object.defineProperty to an array. It is important to understand that these properties will not be part of the array itself so most loop methods like .map .filter or for...of will not loop over these properties, they just loop over the values in the array itself. As before, you can make the property enumerable or not and that decides whether or not it will show in the different object loop methods.

Prototype Chain

The prototype chain is a fundamental concept in javascript. Even though you might not be directly using it, it impacts your day-to-day life even if you don’t notice. Javascript classes are just syntactic sugar on top of prototypes. So let’s understand what we’re talking about.

When you do something like

myObject doesn’t hold the toString method, so the Javascript engine tries to look for it in the prototype chain which in this case looks like

myObject <- Object.prototype <- null

All objects in Javascript inherit properties from a prototype.

  • Date objects inherit from Date.prototype
myDateVariable <- Date.prototype <- Object.prototype <- null
  • Array objects inherit from Array.prototype
myArrayVariable <- Array.Prototype <- Object.prototype <- null
  • myObject objects inherit from myObject.prototype
myObject <- Object.prototype <- null

The Object.prototype is on the top of the prototype inheritance chain and its prototype is null , which is the end of the chain.

Before, we said that functions are objects, but with a little caveat when working with object constructor functions.

You never want to assign properties to an object constructor function outside of the constructor itself. You either want to add it inside the constructor, add it to the instance of the object ( new MyObjectFunc() ) or add it to the prototype of the constructor.

A function instance does not inherit properties from the function itself but from its prototype, that’s why the above code does not work as expected, the prototype chain of the above function constructor looks like this

myObject <- myObjectFunc.prototype <- Function.prototype <- Object.prototype <- null

When you call const myObject = new myObjectFunc() what javascript is doing under the hood is

  1. Creating a new object
  2. Setting the prototype of this object to the constructor function’s prototype property
  3. Binding the this keyword to the newly created object and executing the constructor function
  4. Returning the newly created object

The above snippet is functionally equivalent to

Assigning a Prototype to an Object

As we just saw, you can assign a prototype to an object by calling Object.create() and passing the object to use as a prototype. It will return a new object that has your object as a prototype.

Another method also exists which is Object.setPrototypeOf() but its use is highly discouraged because of performance reasons.

In this case the prototype chain of mySecondObject looks like this

mySecondObject <- myFirstObject <- Object.prototype <- null

Prototypal Inheritance and Prototypal Delegation

Prototypal inheritance states that an object inherits all the properties of its prototype.

There is also another very important concept which is called prototypal delegation which states that when you try to access a property of an object, if it cannot find the property in the object itself, it will traverse the entire prototype chain until it finds that property. If it cannot find it it’ll just return undefined.

Inheriting methods and the “this” keyword

In JavaScript, any function can be added to an object as a property. Inheriting a function works exactly as inheriting a property, including property shadowing (which works kind of like method overriding).

When a property is inherited, the this keyword refers to the context of the parent calling object, not the original object where the property was defined.

As you can see the sayHi() function is called from the mySecondObject context hence the this keyword refers to that. If a hi property is not defined in mySecondObject, it will be looked for in the prototype chain.

Property Shadowing

Because of prototypal delegation, when accessing a property, the Javascript engine will try to look for it in the object itself and if it doesn’t find anything, it will keep looking for it in the prototype and so on until the end of the prototype chain.

What happens if both an object and its prototype have a property with the same name? When trying to access that property, the one in the object itself will shadow the one in the prototype, which will need to be accessed directly using myObject.prototype.myProperty .

Please DO NOT extend native prototypes such as Object.prototype or Array.prototype, it’s considered bad practice and should be avoided.

Conclusion

For a more in-depth reference, please take a look at this link

In the next article, we will see how to correctly compare and copy objects.

--

--

No responses yet