Family photo in the style of the Addams Family, where each is labeled as object

Almost Everything in JavaScript — Object

Whatever we use when writing JavaScript code, almost all of it is objects under the hood.

Time to read: 6 min

Briefly

In JavaScript, an object is the ancestor of all other entities. All data types and structures, except for primitives, are descendants of the object. For this reason, all descendants of the object have a set of common methods: toString(), valueOf(), and others.

How to Understand

Arrays and Functions

Object — is an entity with a set of properties. We can add, change, and delete these properties.

        
          
          const programmer = { name: 'John', level: 'Junior' }programmer.mainLanguage = 'JavaScript'delete programmer.levelconsole.dir(programmer)
          const programmer = { name: 'John', level: 'Junior' }

programmer.mainLanguage = 'JavaScript'
delete programmer.level

console.dir(programmer)

        
        
          
        
      
console displaying the properties of the object from the example

If you look at an array, it also has a set of properties, but its own. For example, an array has a length, methods to work with it. Accessing an array element by index, as can be noticed, is similar to accessing a property of an object using square brackets.

        
          
          const shows =  ['Breaking Bad', 'The Office', 'Silicon Valley']// Property of the arrayshows.length// Get an array element,// similar to how you would with an object shows['1']shows[1]
          const shows =
  ['Breaking Bad', 'The Office', 'Silicon Valley']

// Property of the array
shows.length

// Get an array element,
// similar to how you would with an object shows['1']
shows[1]

        
        
          
        
      

The same situation applies to functions. They also have a set of properties that can be seen by outputting their information to the console.

        
          
          function sum(a, b) {  return a + b}// You can call a property of the functionsum.arguments// You can assign a value to a fieldsum.someField = 'value'console.dir(sum)
          function sum(a, b) {
  return a + b
}

// You can call a property of the function
sum.arguments

// You can assign a value to a field
sum.someField = 'value'

console.dir(sum)

        
        
          
        
      

In the output, there is the property someField, which we assigned, and a set of built-in properties and methods.

console displaying the properties of the function

This structure of arrays and functions is very similar to the structure of objects. But in fact, they are objects. This can be easily verified. Let's look at the property __proto__ of the function sum(), described above.

console displaying the prototype of the sum function

If you look at the prototype property, you can notice that the current prototype is an object. Peeking into this prototype, you can see the following picture:

methods of the object in the console

In this chain, there is no next prototype, which means that we have reached the very end of the chain, that is, found the ancestor. If you output any array to the console in this way, then by descending down the prototype chain, there will definitely be the prototype of the object at the end. Any entity in JavaScript inherits from the object.

prototype chain diagram

Primitives

In JavaScript, there are primitive data types such as strings, numbers, or boolean values. When working with a string, you can discover that it also has properties and methods that can be accessed.

        
          
          const show = 'Breaking Bad'console.log(show.length)// 12console.log(show.charAt(1))// 'r'console.log(show.toUpperCase())// 'BREAKING BAD'
          const show = 'Breaking Bad'

console.log(show.length)
// 12
console.log(show.charAt(1))
// 'r'
console.log(show.toUpperCase())
// 'BREAKING BAD'

        
        
          
        
      

If a string is a primitive data type, where does it get the behavior of an object? When we access a property or method of a primitive, a wrapper is created using a special constructor (autoboxing), which is a descendant of Object. For a string, this will be the function String(). This object has properties and methods that are invoked.

        
          
          const pet = 'dog'// An object will be createdconst pet2 = new String('dog')console.log(pet === pet2)// false, because pet2 contains an objectconsole.dir(pet2)/* Will output{  0: "d",  1: "o",  2: "g",  length: 3}*/
          const pet = 'dog'
// An object will be created
const pet2 = new String('dog')

console.log(pet === pet2)
// false, because pet2 contains an object

console.dir(pet2)
/* Will output
{
  0: "d",
  1: "o",
  2: "g",
  length: 3
}
*/

        
        
          
        
      

For other data types, there are similar functions: Number() for numbers, Boolean() for boolean values. All these functions are also descendants of the object.

The main difference between objects (arrays, functions) and primitives is that primitives are immutable. Trying to change or add properties to a primitive does nothing.

        
          
          const cat = 'Boris'// The property won't be addedcat.color = 'red'// It won't change anything eitherdelete color.lengthconst cats = ['Boris', 'Vasya', 'Murzik']// Now the array has a length of five elementscats.length = 5// A field has been addedcats.someField = 'value'console.dir(cats)/*{  0: "Boris",  1: "Vasya",  2: "Murzik",  someField: "value",  length: 5}*/
          const cat = 'Boris'

// The property won't be added
cat.color = 'red'
// It won't change anything either
delete color.length

const cats = ['Boris', 'Vasya', 'Murzik']
// Now the array has a length of five elements
cats.length = 5
// A field has been added
cats.someField = 'value'

console.dir(cats)
/*
{
  0: "Boris",
  1: "Vasya",
  2: "Murzik",
  someField: "value",
  length: 5
}
*/

        
        
          
        
      

Do not confuse a primitive with an object created through a constructor for that primitive:

        
          
          const cat = new String('Boris')cat.color = 'black'// It will be added, as cat holds an object, not a string
          const cat = new String('Boris')
cat.color = 'black'
// It will be added, as cat holds an object, not a string

        
        
          
        
      

How It Is Written

Fields and methods of objects and arrays can always be invoked: through a variable and inline, meaning without using a variable.

        
          
          const array = [1, 2, 3, 4]console.log(array[1])// 2const pos = 3console.log(array[pos])// 4console.log(array.map(a => a + 1))// [2, 3, 4, 5]const f = 'map'console.log(array[f](a => a + 1))// [2, 3, 4, 5]const obj = { name: 'Boris', color: 'red' }console.log(obj.color)// 'red'console.log(obj['name']);// 'Boris'const age = Object.assign(obj, {  name: 'Vasya',  age: 30}).ageconsole.log(age)// 30
          const array = [1, 2, 3, 4]
console.log(array[1])
// 2
const pos = 3
console.log(array[pos])
// 4

console.log(array.map(a => a + 1))
// [2, 3, 4, 5]
const f = 'map'
console.log(array[f](a => a + 1))
// [2, 3, 4, 5]

const obj = { name: 'Boris', color: 'red' }
console.log(obj.color)
// 'red'
console.log(obj['name']);
// 'Boris'

const age = Object.assign(obj, {
  name: 'Vasya',
  age: 30
}).age
console.log(age)
// 30

        
        
          
        
      

Almost all primitives can also have methods accessed without a variable:

        
          
          true.toString()// 'true'Infinity.toString()// 'Infinity''hello world'.toString()// 'hello world'Symbol('tag').toString()// 'Symbol(tag)'9007199254740991n.toString()// '9007199254740991'
          true.toString()
// 'true'

Infinity.toString()
// 'Infinity'

'hello world'.toString()
// 'hello world'

Symbol('tag').toString()
// 'Symbol(tag)'

9007199254740991n.toString()
// '9007199254740991'

        
        
          
        
      

However, in the case of numbers, you can get a syntax error because the dot is interpreted as part of the number itself:

        
          
          42.toString()// Uncaught SyntaxError:// Invalid or unexpected token
          42.toString()
// Uncaught SyntaxError:
// Invalid or unexpected token

        
        
          
        
      

To avoid this, you can use two dots, wrap the expression in parentheses, or call the wrapper of the primitive type:

        
          
          42..toString()// '42'(42).toString()// '42'Number(42).toString()// '42'
          42..toString()
// '42'

(42).toString()
// '42'

Number(42).toString()
// '42'

        
        
          
        
      

Calling methods or properties will not work on null and undefined:

        
          
          null.toString()// Uncaught TypeError:// Cannot read property 'toString' of nullnull.valueOf()// Uncaught TypeError:// Cannot read property 'valueOf' of nullnull.length// Uncaught TypeError:// Cannot read property 'length' of nullundefined.toString()// Uncaught TypeError:// Cannot read property 'toString' of undefinedundefined.valueOf()// Uncaught TypeError:// Cannot read property 'valueOf' of undefinedundefined.length// Uncaught TypeError:// Cannot read property 'length' of undefined
          null.toString()
// Uncaught TypeError:
// Cannot read property 'toString' of null

null.valueOf()
// Uncaught TypeError:
// Cannot read property 'valueOf' of null

null.length
// Uncaught TypeError:
// Cannot read property 'length' of null

undefined.toString()
// Uncaught TypeError:
// Cannot read property 'toString' of undefined

undefined.valueOf()
// Uncaught TypeError:
// Cannot read property 'valueOf' of undefined

undefined.length
// Uncaught TypeError:
// Cannot read property 'length' of undefined

        
        
          
        
      

In practice

Advice 1

🛠 Very rarely is it necessary to call the methods of an object or primitive without using a variable (as in the examples from the article). This negatively impacts the readability of the code. It is better to always use variables to store values. This way, you can safely call methods of both the object and the primitive, and JavaScript will figure out what to do to produce the desired result.