Javascript Objects

JavaScript Objects

JavaScript objects are not primitive data types because they can store complex and mixed data that can be changed.

JavaScript objects can be created using curly braces { } and an optional list of properties inside. A property is a variable of an object and is in form of a "key: value" pair. The key is the "property name" and is a string whilst the value can be any data type.

An empty object can be created in as follows:

// using new object constructor
let serviceModule = new Object();

// OR

// using object literal
let commandModule = {}; 

Literal Properties

Literal Properties

To understand JavaScript objects think of them as everyday objects. Let me give you an example using a car as an object. Lets say you buy a new car today and you want to describe it to someone over the phone. You talk about its color, number of seats, model, etc. The car is the object, number of seats, color and anything that you can describe the car with are properties.

Lets define the car object and add its properties:

let car = {};
car.color = "red";
car.seats = 4;
car.model = "Sytech Van";

In the example above we defined the car as an empty object and then added the properties. Alternatively we can defined the car with its properties at the same time as shown below:

let car = {
color : "red",
seats : 4,
model : "Sytech Van"
}

In both examples we have "color", "seats" and "model" as property names ( also knows as keys or property identifiers). "red", 4, "Sytech Van" are property values.

Now that we have our object, we can read it as simply as follows:

let car = {};
car.color = "red";
car.seats = 4;
car.model = "Sytech Van";

console.log(car); // { color: 'red', seats: 4, model: 'Sytech Van' }

Property values can be read using the dot notation:

let car = { 
	color : "red", 
	seats : 4, 
	model : "Sytech Van" 
	}

console.log(car.color); // red
console.log(car.model); // Sytech Van

Now continuing with our car example; someone asks if the car you bought is second hand or new you can add the used property using the dot notation.

let car = { 
	color : "red", 
	seats : 4, 
	model : "Sytech Van"
	}

// add another property
car.used = false;

console.log(car);   // { color: 'red', seats: 4, model: 'Sytech Van', used: false }

The used property is a boolean type that we added to represent the state of the car. You can add any property you want using the dot notation as long as the key is a valid identifier.

As time goes on, you decide to change to change the color of the car. You can change the value of a property using the dot notation as follows:

let car = { 
	color : "red", 
	seats : 4, 
	model : "Sytech Van"
	}

// the value of a property
car.color = "blue";

console.log(car);   // { color: 'blue', seats: 4, model: 'Sytech Van' }

A property can be removed using the delete operator:

let car = { 
	color : "red", 
	seats : 4, 
	model : "Sytech Van"
	}

// delete a property
delete car.seats;

console.log(car);   // { color: 'red', model: 'Sytech Van' }

console.log(car.seats); // undefined

Multiword property names are allowed, but they have to enclosed in quotes:

let car = { 
	color : "red", 
	seats : 4, 
	model : "Sytech Van",
	"number of wheels": 4
	}

console.log(car);   

/* OUTPUT

 {  color: 'red',
  seats: 4,
  model: 'Sytech Van',
  'number of wheels': 4 }
  
*/

However you can’t access a multiword property using the dot notation. This results in an error as shown below:

let car = { 
	color : "red", 
	seats : 4, 
	model : "Sytech Van",
	"number of wheels": 4
	}

console.log(car."number of wheels");

/* OUTPUT

console.log(car."number of wheels");
                ^^^^^^^^^^^^^^^^^^

SyntaxError: Unexpected string
*/

The dot notation only works if the property is a valid indentifier. A quoted string is not a valid identifier. For a property that is not a valid identifier we use the square bracket notation (computed property operator). For example:

let car = { 
	color : "red", 
	seats : 4, 
	model : "Sytech Van",
	"number of wheels": 4
	}

console.log(car["number of wheels"]); // 4

Computed properties

Computed properties are properties whose name can be defined elsewhere and then used in an object using square brackets.

For example:

let unknownProperty = "speed";

let car = { 
	[unknownProperty] : 80,
	color : "red", 
	seats : 4, 
	model : "Sytech Van",
	"number of wheels": 4
	}

console.log(car);

/* OUTPUT 

{ speed: 80,
  color: 'red',
  seats: 4,
  model: 'Sytech Van',
  'number of wheels': 4 }
  
*/
};

In the example above, the property name speed was not hardcoded inside the object but "computed" from the value of variable unknownProperty.

Reserved words as property keys

Even though reversed words cant be used as variable identifiers, they can be used as properties keys perfectly fine:

let car = { 
	run : 80,
	for : "Sytech Van",
	var : 4,
	let : 6
	}

console.log(car); // { run: 80, for: 'Sytech Van', var: 4, let: 6 }

Checking if a property exists

In JavaScript there is no error if you try to access a non-existant object property. undefined will be returned.

let car = { 
	used : false
	}

console.log(car.mileage); // undefined

This can be used be used to check if property exists. If it returns undefined it doesn’t exist. Another method of checking if a property exists is to use the "in" operator.

let car = { 
	used : false,
	seats : 4
	}

console.log("mileage" in car); // false
console.log("seats" in car); // true

The left side of in operator is usually property name represented as a quoted string.

Earlier on we learnt that a accessing a non-existent property returns undefined. This is ambiguous because even a property explicitly defined as undefined returns undefined. However the in operator differentiates the two.

let car = { 
	used : false,
	seats : 4,
	engine: undefined
	}

console.log(car.milage) // undefined
console.log(car.engine) // undefined

console.log("mileage" in car); // false
console.log("engine" in car);  // true

The for..in loop

The for..in loop is a special loop designed to loop through the object keys.

Syntax:

for(key in object) {
  
}

For example lets write a loop that prints all the keys of an object:

let car = { 
	used : false,
	seats : 4,
	engine: undefined
	}

for ( prop in car ){
console.log(prop);
}

/* OUTPUT

used
seats
engine

*/

We can use square brackets as follows to get the values of the properties:

let car = { 
	used : false,
	seats : 4,
	engine: undefined
	}

for ( prop in car ){
console.log(car[prop]);
}

/* OUTPUT

false
4
undefined

*/

Object properties order

Here is what happens when you loop through an object:

  • integer property keys are sorted in ascending order. This is true even for integers in quotes.
  • other property key types are sorted in order of creation.

For example:

let cars = {
  "12": "Toyota",
  "27": "Mazda",
  "34": "Volvo",
  "23": "Sytech"
};

// order in ascending order
for(let car in cars) {
  console.log(car); // 12 23 27 34
}

for(let car in cars) {
  console.log(cars[car]); // Toyota Sytech Mazda Volvo
}
let cars = {
  a: "Toyota",
  c: "Mazda",
  d: "Volvo",
  b: "Sytech"
};

// ordered in order of creation
for(let car in cars) {
  console.log(car); // a c d b
}

for(let car in cars) {
  console.log(cars[car]); // Toyota Mazda Volvo Sytech
}

Copying by reference

Objects are copied by reference which is different from primitives that are copied by value.

If you copy a primitive (string, number or booleans) only the value is copied. This means that if we change anything in the copy, the original primitive doesn’t change.

If you copy an object, the reference to the object is copied. The resulting two objects refer to the same data. This means that if we change the second object, the first object also changes.

All this is illustrated in the example below:

//////////////
// PRIMITIVES

// create variable
let car1 = "Mazda";

// assign the second variable to the first
let car2 = car1;

// change the second variable
car2 = "Toyota";

// the first variable doesn't change
console.log(car1); // Mazda


//////////
// OBJECTS

// create an object and add a property
let bus1 = {};
bus1.name = "Scania";

// assign the second object to the first
let bus2 = bus1;

// change a property from the second object
bus2.name = "Volvo";

// the first object changes too
console.log(bus1.name); // Volvo

In short, an object variable is copied by reference, not duplicated whilst a primitive is copied by value and duplicated.

Equality of objects

Comparing whether by the equality == or the strict equality === operators, no two objects are equal unless they refer to the same object.

Here is an example to show that two identical independent objects are not equal:

// create two empty objects
let car1 = {};
let car2 = {};

console.log(car1); // {}
console.log(car2); // {}

// they are both empty but still not equal
console.log(car1 == car2); // false
console.log(car1 === car2); // false

// add the same property to both
car1.color = "blue";
car2.color = "blue";

// the properties are equal
console.log(car1.color == car2.color); // true
console.log(car1.color === car2.color); // true

// the two objects are identical
console.log(car1);  // { color: 'blue' }
console.log(car2);  // { color: 'blue' }

// but still they are not equal
console.log(car1 == car2); // false
console.log(car1 === car2); // false

Here is an example to show equal objects


let car1 = {};
let car2 = car1;

// now they are equal
console.log(car1 == car2); // true
console.log(car1 === car2); // true

Const object declaration

To see the difference between let and const object declarations, lets look at the following examples:

Example 1:

// assign planet object
let planet = {
  name : "Mercury"
};

console.log(planet);  // { name: 'Mercury' }

// reassign the planet object
planet = { 
  name : "Venus"
};

console.log(planet); // { name: 'Venus' }

Example 2:

// assign planet object
const planet = {
  name : "Mercury"
};

console.log(planet);  // { name: 'Mercury' }

// reassign the planet object
planet = { 
  name : "Venus"
};

console.log(planet); // TypeError: Assignment to constant variable.

When an object is created using a const declaration, it can’t be reassigned again. However even though we cant reassign it, we can still add, remove or change its properties.

const planet = {
  name : "Mercury"
};

planet.position = 1;

console.log(planet); //   { name: 'Mercury', position: 1 }

planet.name = "Earth";

console.log(planet);  //  { name: 'Earth', position: 1 }

delete planet.name;

console.log(planet);  //  { position: 1 }

Object cloning

In JavaScript, objects are copied by reference and when we want to create an independent clone of the object we can copy the properties manually with a loop.

For example, lets create a planet object, then create an independent clone of it.

let planet = {
  name : "Mars",
  neighbor: "Earth",
  owners : "Little green men"  
};

let mirrorPlanet = {};
for ( let prop in planet ){
mirrorPlanet[prop] = planet[prop];
}

console.log(mirrorPlanet);  //  { name: 'Mars', neighbor: 'Earth', owners: 'Little green men' }

In the example above, the two objects created are independent and do not have the same reference. Therefore a change in one object wont affect the other.