📒
SDV503
  • SDV503
  • Command Prompt
    • Windows Command Prompt
    • Mac OS Terminal
  • GIT & GITHub
    • GitHub
    • Git
      • Git Workflow
        • Forking Workflow
  • README
    • How to write a readme.md file?
    • Write a better README.me
    • Generate a README for GitHub Repo Quickly
  • Code-comments
    • Clean Comments
    • Writing Comments in a Good way
  • Pair Coding
    • What is Pair Coding?
    • Pair Programming Experience
  • Programming?
    • What is Programming?
    • What Is A Programming Paradigm?
    • JavaScript Programming Paradigms
  • Number Systems
    • Decimal and Binary numbers
  • JavaSCript
    • Introduction To JavaScript
      • The JavaScript Engine
  • JS Call Stack
    • JavaScript call stack
    • JavaScript & Memory
      • Memory Leaks in JavaScript
    • Execution Context and Execution Stack in Javascript
      • Javascript Execution Context and Hoisting
  • JavaScript Variables
    • Introduction to JS Data Types
    • Primitive and Non-Primitive
    • Operator precedence and associativity
      • JS Operators Part One
      • JS Operators Part Two
      • JS Operators Part Three
    • Clean Code for JS Variables
  • JavaScript Scopes
    • Scope (Chain) Visualized
  • Javascript  — this keyword
  • JavaScript Data Types
    • More JavaScript Data Types
  • JavaScript Expressions and Statements
  • if/else, switch and ternary operator
    • If/Else statement
  • Switch Statement
  • Ternary Operator
  • JavaScript Loops
    • Loops in JavaScript
      • Trouble With Loops
  • Objects
    • JavaScript Objects
      • Prototypal Inheritance Visualized
      • JavaScript Number Object
      • JavaScript String Object
  • Functions
    • JavaScript Function Part One
    • JavaScript Function Part Two
    • Immediately Invoked Function Expressions ~ IIFE
    • JS => Arrow Functions
    • JavaScript Callback
    • Hoisting in JavaScript
      • Hoisting Visualized
    • Recursion Functions
    • Curry and Function Composition
  • JavaScript Features
    • JSpread Operator
    • JavaScript Built-in Functions & Objects
  • Data Structures&Algorithms
    • JavaScript’s Data Types
    • Data Structures in JavaScript
      • Introduction to Big O Notation
      • Big O Notation in Javascript
      • Linked Lists
        • Linked Lists — 2
      • Hash Tables
      • Stack & Queue
  • TLDR
    • Single quotes (‘ ’) and double quotes (“ ”) in JavaScript
  • ES6
    • Generators and Iterators
    • Javascript Classes
    • JavaScript closures
    • JavaScript Promises & Async/Await
      • Event Loop Visualized
  • C#
    • What does C#? (C Sharp)
    • C# vs JavaScript
    • What Is The Difference Between C#, .NET, ASP.NET, Microsoft.NET, and Visual Studio?
    • What is the .NET Framework?
    • Methods and Properties of Console Class in C#
    • Datatypes in C#
    • C# Code Comments
    • The if statement
    • The switch statement
    • Loops
    • Comparison operators
    • Addition assignment operators
    • The String Interpolation Operator
    • Arrays
    • Lists
    • Dictionaries
Powered by GitBook
On this page
  • What are objects?
  • Objects and Properties
  • Creation
  • Creating an object using “literal” notation.
  • Creating objects using Object constructor
  • Computed Properties
  • Function as key values
  • Dot Notation
  • Bracket Notation
  • Reading/Retrieving
  • A Primer on Prototypes
  • Undefined
  • in operator
  • hasOwnProperty
  • Updation
  • Deletion
  • for-in loops
  • Object.keys()
  • Object.values()
  • Object.entries()
  • Reflect.ownKeys()
  • Comparison
  • Object Spread Operator
  • JSON.stringify
  • Object.assign
  • What about arrays?
  • And functions?
  • Exercises

Was this helpful?

  1. Objects

JavaScript Objects

PreviousTrouble With LoopsNextPrototypal Inheritance Visualized

Last updated 4 years ago

Was this helpful?

Objects are the foundation of JavaScript and permeate its every aspect. Almost everything in JavaScript is an object. In fact, only six things are not objects. They are — null, undefined,strings, strings, numbers, Boolean, and symbols. These are called primitive values or primitive types.

Anything that is not a primitive value is an Object. That includes arrays, functions, constructors, and objects themselves. Yes! Functions and arrays are objects too.

What are objects?

Just like other primitive values, objects are values too and they serve the same purpose: Representing information or data. When we create a variable and assign a value to it, we are holding information in it for later retrieval and processing. For example —

const firstName = 'Arfat Salman';

In this case, firstName holds and represents the first name of a person. In a sense, strings are called primitive values because they can represent only one type of information. In this case, a collection of characters or a string.

What happens when we want to represent a person as a whole? That is, I want to hold on to first name (String), last name (String), date of birth (Date), and her friends (Collection of Strings). If I create four variables for each datum, representing three persons would require 4 * 3 = 12 variable names. That’s not good!

In some languages, this question would segue into a discussion about user-defined data types or custom data types. Constructs such as classes, , unions types etc. can be used to achieve that. However, JavaScript does not have classes (even though it has a class keyword), or structures. It only has objects for creating custom data types and associating behavior.

In JavaScript, an object can be thought of as a distinct entity that can have properties associated with it. These properties define the object’s characteristics and behavior. In our person example, an object can have 4 properties describing it — first name, last name, DOB, and friends.

Here’s how ECMA specification defines an object —

An Object is logically a collection of properties.

Objects and Properties

We are going to examine objects by the kind of operation we can perform on them. The operations are —

  • Creation — Creating a new object.

  • Addition — Adding properties to an object.

  • Reading/Retrieving — Reading the value associated with a key.

  • Existence — Whether a property exists in an object.

  • Updation — Updating the values of existing properties.

  • Deletion — Deleting existing properties.

  • Iteration (Enumeration) — Looping over an object’s keys and values.

  • Comparison — Comparing two different objects.

  • Copying — Making a copy of an existing object

Creation

Properties of an object are just like variables. Let’s see how can we create objects and properties —

Creating an object using “literal” notation.

A literal is a notation for representing a fixed value in source code. For example, 5 or "foo". They literally represent the value. An object is represented as —

const obj = { }; // Creating an object using literal notation

The curly braces { } create a new object.

You can also specify properties to be inserted at the time of creation.

const obj = {
  firstName: 'Alex',
  'lastName': 'Martin', // Using string quotes
  dateOfBirth: '18th October',
  friends: [ 'Bob', 'Christine' ]
};

The property names are treated as strings. That is, firstName and 'firstName' are same. An object property name can be any valid JavaScript string, or anything that can be converted to a string, including the empty string.

Note that the values can be any value type. That is, both primitive types and an object can be used as values.

const obj = {
   nestedObj: {
      name: 'Alex'
   }
}

The nestedObj is a key that holds another object. The nesting can be arbitrary and cyclic too!

Creating objects using Object constructor

const obj = new Object();

You can also use an expression that will be evaluated to a string using the new computed property syntax. As an example —

const propertyName = 'firstName';
const obj = {
  [propertyName.toUpperCase()]: 'Alex',
}// { FIRSTNAME: 'Alex' } 

Note the variable propertyName. In obj, we wrap the expression propertyName.toUpperCase() in [ ] to make it a computed property. As expected, in the resulting output, the stringfirstName is turned into uppercase.

Function as key values

In an object, when we have a function as a value to a key, then the function is called a method. This is one of the most important features of objects. The methods have access to the key-value pairs of the object. They can specify behavior in an object.

const obj1 = {
  firstName: 'ALEX',
  lastName: 'MARTIN',
  printFullName() {
    return `${this.firstName} ${this.lastName}`
  }
};
obj1.printFullName();
//=> 'ALEX MARTIN'

After we have created our object, we can add properties to it. There are two distinct ways of adding properties —

Dot Notation

obj.address = 'Earth';

The object and the property name is separated by a . (dot). If a property with the same name exists, its value will be overwritten. Otherwise, a new property with property name address will be added to the object.

An object can only contain a single key with one value. We can’t have a single key having two different values.

Caveat: Using the dot-notation, you can’t use property names that are numbers, or string that contain spaces, or special character, or dynamic properties. For example, these will throw a SyntaxError.

obj.5 = 'five'; // Using numbers as property names
obj.first name = 'alex' // Using spaces between property names
obj.first-name = 'alex' // Using special characters -

Bracket Notation

If you don’t want to be limited by the above caveats, you may consider using bracket notation.

obj["address"] = 'Earth' // Note the quotes 

Here we are adding address property to the obj object.

A few things to note —

  • The part between the brackets is an expression. That means we can use variables there. See the example below.

const propName = 'address';
obj[propName]  = 'Earth' // No string quotes

Executing obj.propName adds a property propName whereas obj[propName] adds a property after evaluating the variable propName. The content of propName is address, so a property address with the value Earth is added to obj.

Also, these are now valid —

obj[5] = 'five'; // 5 is coerced to a string to make '5'
obj['first name'] = 'alex'; // property as string
obj['first-name'] = 'alex'; // property as string

Reading/Retrieving

Retrieving a property is by far the most complex to understand in JavaScript. Let’s see some simple examples —

obj.address         // => 'Earth'
obj['firstName']    // => 'Alex'
obj['middleName']   // => undefined

Like addition, we can use the dot-notation or bracket notation get the value of a property. If the property exists, we get its value. If the property does not exist, we get undefined.

A Primer on Prototypes

Objects are not just containers of key-value pairs. They have one more very interesting property. They can have a parent. That is, they store a link to another object. This parent object is also consulted when a property is read.

And not just that. That parent can also have a parent. And that grandparent object is consulted too when a property is read. This repeats until we reach an object that does not have any parent i.e the parent is null.

Here’s the rough algorithm used when we retrieve a property —

Goal: Read middleName from the object obj. (Note that middleName does not exist in obj )

(Rough) Algorithm —

  • Let CurrentObject = obj. Let CurrentKeyName = middleName.

  • If CurrentObject has CurrentKeyName, return associated value.

  • Else, let CurrentObject = parent of CurrentObject.

  • Check for CurrentKeyName again in CurrentObject. If found, return value.

  • If CurrentObject has no parent and does not have CurrentKeyName, return undefined.

In the last step, CurrentObject would contain the final parent-less object. By this time, we have checked every object in the chain and none of them have the property that we are looking for. If the property is found at any level, its value is returned.

This chain is called the “prototype” chain. By default, all objects have their parent as Object.prototype. There is no parent to Object.prototype. That’s where the chain ends, normally.

The [[Prototype]] property keeps this reference to the parent object. It’s an internal property which means that we can’t access it directly. We can use Object.getPrototypeOf function to get the parent (aka “prototype”) of an object.

An important point to note is that the prototype chain is consulted only while reading a value. It does not affect addition, updating or deletion of properties on parent objects. One way to look at this is — You have read-only access to all things your parent (and their parents) have.

Undefined

If the property does not exist in an object(or its parents), we get undefined. What happens when we intentionally set a property’s value to undefined?

obj.firstName = undefined;

In this case, we have the property but its value is purposely not defined. When we retrieve the value, we’ll get undefined. But how can we be sure that its value in intentionally undefined or the property does not exist on the object?To ascertain that we need to show the existence of a property.

Sometimes we don’t care what value a property has. We only want to know whether the given object has the given property or not. There are 2 ways to know that —

in operator

'firstName' in obj;       // => true
'middleName' in obj;      // => false 
'isPrototypeOf' in obj;   // => true

The syntax is String (or Symbol) in object. It will evaluate to true or false depending on whether the property exists.

Attentive readers may be surprised to see the last example ( 'isPrototypeOf' in obj ) evaluate to true. This is the gotcha of in operator.

isPrototypeOf is a property of the parent of obj. Just like Reading a property, the entire prototype chain is consulted before true or false is returned. If the property is found at any level, true is returned.

What if we want our own properties? Properties that exist only on the current object in question irrespective of whether that same property exists on parents or not.

hasOwnProperty

It’s a function that can be accessed through an object because of prototypes. It takes string key name ( or symbol) as an argument.

obj.hasOwnProperty('firstName');        // => true
obj.hasOwnProperty('middleName');       // => false
obj.hasOwnProperty('isPrototypeOf');    // => false

Updation

Updating is simple. It’s just like addition. You can use dot-notation or bracket-notation. Here’s an example —

obj.firstName = 'Timothy'
obj['firstName'] = 'Timothy'

It has the side effect of creating a new property firstName and setting the value Timothy if the property did not exist. If you don’t want that, you should pair your code with existence checks.

Also, updating a property always affects the object on which it is performed. It does not modify any object in the prototype chain.

Deletion

Deletion is performed using thedelete operator. Again, we can use both the notations.

delete obj.firstName;      // => true
delete obj['firstName'];   // => true

The return value of delete operator is true if the property was successfully deleted. Else, it will be false. We can make properties un-deletable. So, it is important to check the output of delete.

Also, merely setting a property’s value to undefined won’t delete the said property.

An object is a collection of key-value pairs. Often times you want to process the set of all values or keys and create another object with transformed values or key names. Unlike arrays, you can’t simply iterate an object. Here are a few ways to iterate objects —

for-in loops

for-in loop is just like a normal for loop except that it iterates on an object. You get the properties one by one. Here’s an example using obj object —

for (const property in obj) {
  const value = obj[property]; // Read the value 
  console.log(property, value);
}// firstName Alex
// lastName Martin
// dateOfBirth 18th October
// friends [ 'Bob', 'Christine' ]

Note —

  • The order of appearance of properties is not fixed in an object.

  • for-in loop will return all string properties. It won’t give Symbol properties.

  • The prototype chain is consulted, and all enumerable properties of parents are also returned. (In short, enumerable properties are those properties which are visible to looping constructs.)

Because the prototype chain is consulted, we may get unwanted properties in a for-in loop. We can put an existence check using hasOwnProperty to only get own properties, but we have better ways to do the same thing now.

Object.keys()

Object.keys() is similar to for-in loop except that it only returns the object’s own keys in an array. That is, the prototype chain is not consulted. We can then iterate on this array using a for-of or a for loop. Here’s an example —

const allProperties = Object.keys(obj); // => [ 'firstName', 'lastName', 'dateOfBirth', 'friends' ]
for (const property of allProperties) {
  const value = obj[property];
  console.log(property, value);
}

Attentive readers will see that we have to manually read the value inside the loop using const value = obj[property];. What if we only want the values?

Object.values()

Object.values() has similar restrictions as Object.keys(), but it returns an array of values instead of the keys.

const allValues = Object.values(obj);// => [ 'Alex', 'Martin', '18th October', [ 'Bob', 'Christine' ] ]

What if we want both values and properties at the same time?

Object.entries()

Same as .keys() and .values() except that we get an array of [key, value] pairs.

const allEntries = Object.entries(obj);// => Output[ [ 'firstName', 'Alex' ],
  [ 'lastName', 'Martin' ],
  [ 'dateOfBirth', '18th October' ],
  [ 'friends', [ 'Bob', 'Christine' ] ] ]
for (const [key, value] of Object.entries(obj)) {
  console.log(key, value);
}

One restriction the above methods pose is that they only return string properties and not symbols in an object. What if we want both?

Reflect.ownKeys()

ownKeys return both string-based and symbol properties. Note that we are using Reflect module, and not Object module.

Comparison

There are no good in-built of ways of comparing two objects. When you use the == or === operators, they only compare the references of the objects. References can be understood as “memory addresses” of the objects. Only primitives types are compared by values.

Now, two objects can have the same key-values pairs but they can’t occupy the same location in memory or reference. The object is only equal to itself as the example shows us —

const obj1 = {
  name: 'Alex',
}const obj2 = {
  name: 'Alex',
}obj1 === obj2    // => false
obj1 === obj1    // => true

See exercises below for a solution to this.

This is another hard aspect of objects. When we use the = operator on an object, we merely copy its reference. Let’s see this by an example —

const obj1 = {
  name: 'Alex',
}const obj2 = obj1;

There are multiple ways to do a deep copy —

Object Spread Operator

We can use ... operator to a top-level deep copy. Here’s an example —

const obj1 = {
  name: 'Alex',
  nestedObj: {
    address: 'Earth'
  }
}

  obj2 = {
  ...obj1      // Spreading the properties of obj1
}

However, this is only effective for the top-level primitive properties. In this case, obj2 gets its own name property. However, nestedObj is still copied shallowly.

Also, if the value of a property is a function then we can’t create a copy of function reliably in JavaScript. Hence, the functions/methods have to be shared.

JSON.stringify

We take a JSON representation of an object and parse it again to create another object with the same key-value pairs. This has the nice property that nested objects are also deeply copied.

const obj2 = JSON.parse(JSON.stringify(obj));

Object.assign

The assign function takes a source and a target object. It copies all the enumerable, own key-value pairs in the target from the source.

const obj2 = Object.assign({}, obj1);

In this case, the source is obj1 and the target is an empty object { } .

Just like object spread operator, assign does not copy deeply on all levels and both String and Symbol properties are copied.

What about arrays?

Arrays are objects too. The only difference is that the keys are predetermined to be whole numbers (+0 ≤ i < 2³²–1). They are 0, 1, 2 … etc. by default. Also, while creating an array, the keys are automatically set. If we check using typeof [], we will get object.

const arr = ['Alex', 'Earth'];typeof arr;       // => objectObject.entries(arr);  
// [ [ '0', 'Alex' ], [ '1', 'Earth' ] ]

Arrays have length property denoting the number of items in the array that is managed by JavaScript. From the example, we can see that Object.entries return the key-value pairs of the arr object.

And functions?

They are objects too. Specifically, they are objects with internal (not accessible to us) [[Call]] method. Let’s see —

function func1() {
  return 'Alex';
}typeof func1;    // => 'function'func1.name;      // => 'func1'func1.address = 'Earth'; // Setting our own propertyfunc1.address;      // => 'Earth' 
func1['address'];   // => 'Earth'

All functions have certain properties on them by default. name is one of them which stores the name of the function. In this case, name gives us func1 which was the original name of the declared function.

We can also add our own properties. In the example, address was added to func1 and later read like a normal property.

We have discussed quite a few things about objects. However, we have barely scratched the surface. There is another layer to JavaScript objects that we haven’t discussed. Hopefully, we’ll see that in a different article. In the meantime, here are a few exercises to cement your understanding of how objects work.

Exercises

  • Given an object, make a copy of the object with all key names capitalized.

const obj = { name: 'Alex' };
const newObj = capitalize(obj);// => { NAME: 'Alex' }
  • Create an equivalent of Array.map and Array.filter for objects. The callback will receive both key and value. See example below —

const obj1 = {
  firstName: 'ALEX',
  lastName: 'MARTIN'
};const newObj = objectMap(obj1, (key, value) => {
  return [
    key.toUpperCase(),
    value.toLowerCase(),
  ];
});// => { FIRSTNAME: 'alex', LASTNAME: 'martin' }

What happens when the keys are Symbols?

The properties are also known as key-value pairs. You may come across similar concepts in the form of , maps, and dictionaries in other languages. JavaScript objects embody those characteristics, but they also have a lot more to them as we shall see.

This effect of this code and the literal notation is the same. However, it is advised not to use this pattern. You can read more about it .

We add printFullName method on obj1. The method has access to the properties of the object using the this keyword. We return the full name formatted using . In this case, the behavior is to print the full name.

The type of properties can be either astring or a. If the property is an expression, it will be evaluated. If the property is of any other type, it will be coerced to a string value.

This gives more predictable results. However, it’s not without its own set of gotchas. Read about them .

You can use and for-of loop to iterate entries very cleanly.

We create an object obj1. When we do obj2 = obj1, obj2 does not get another new object with its own key-value pairs. Rather, it starts pointing to the object pointed by obj1. This is the core difference between and a . In deep copy, you would get another object with its own set of key-value pairs (having the same value as original ones).

One drawback is that keys whose values are undefined, or functions or a Symbol are skipped. It has many more rules that you can read .

We declare func1 as a function. If we do typeof func1, we get function instead of object. However, that is because the so.

Write a function to two objects by value. What happens when the object’s values are functions? What about symbols?

Associative Arrays
here
Computed Properties
template literals
symbol
here
array “destructuring”
shallow copy
deep copy
here
ECMA Specification says
deep compare
structures