Main menu

Pages

Deep Dive into JavaScript Objects: Working with Complex Data Structures

1. Introduction

JavaScript objects form an integral component of the language, serving as a foundation for organizing and manipulating data effectively. Proficiency in working with objects unlocks the potential to construct intricate applications and harness the complete capabilities of JavaScript.

In this guide, we will explore various aspects of JavaScript objects and dive into their key features, including properties and methods. We'll learn how to create objects, access and modify their properties, and perform operations such as adding, removing, and iterating over properties.

2. JavaScript Objects

In JavaScript, objects are fundamental data structures that allow you to store and organize related data and functions together. An object is a collection of key-value pairs, where each key is a unique identifier for a value or a function, known as properties or methods, respectively. Objects provide a powerful way to model real-world entities and their characteristics.

2.1. Definition of JavaScript Objects and their Purpose

JavaScript objects store data as key-value pairs, where the key acts as the identifier to access the corresponding value or function. The value can be of any JavaScript-supported data type, including strings, numbers, booleans, arrays, or even other objects. Consider the following example:


// Creating an object with key-value pairs
const racer = {
  name: "Lewis Hamilton",
  team: "Mercedes",
  carNumber: 44,
  championships: 7,
};


In the above example, the object racer has properties such as name, team, carNumber, and championships, each associated with a specific value.

2.2. Key-Value Pairs and Properties in Objects

JavaScript objects store data as key-value pairs, where the key acts as the identifier to access the corresponding value or function. The value can be of any JavaScript-supported data type, including strings, numbers, booleans, arrays, or even other objects.

2.3. Creating Objects Using Object Literals:

One common way to create objects in JavaScript is by using object literals, which provide a concise syntax for defining objects. Here's an example:


// Creating an object using object literal
const team = {
  name: "Red Bull Racing",
  country: "Austria",
  activeSince: 2005,
  championships: 4,
};


In this example, we create an object team with properties like name, country, activeSince, and championships. Each property is defined within curly braces {}, with the key-value pairs separated by colons :.

2.4. Accessing Object Properties with Dot Notation and Bracket Notation:

To access the values stored in object properties, you can use dot notation or bracket notation. Dot notation involves using the dot operator . followed by the property name. Here's an example:


// Accessing object properties with dot notation
console.log(racer.name); // Output: Lewis Hamilton
console.log(racer.team); // Output: Mercedes


In this case, we access the name and team properties of the racer object using dot notation.

Alternatively, you can use bracket notation by enclosing the property name in square brackets []. This allows you to dynamically access object properties using variables or expressions. Here's an example:


// Accessing object properties with bracket notation
const propertyName = "name";
console.log(racer[propertyName]); // Output: Lewis Hamilton

const championshipYear = "championships";
console.log(racer[championshipYear]); // Output: 7


In this example, we use bracket notation to access the name property by storing the property name in the propertyName variable. We can also use bracket notation to access properties dynamically, such as accessing the number of championships using the championshipYear variable.

Note: Objects provide a flexible and powerful way to structure and organize data, enabling you to build complex applications. Experiment with different object properties and access methods to deepen your understanding of JavaScript objects.

3. Working with Object Properties

3.1 Modifying Object Properties

When you work with JavaScript objects, it's common to modify their properties dynamically. Let's explore how to do this with three examples:

Example 1:


const player = {
  name: "Valentino Rossi",
  age: 43,
};

player.age = 44;
console.log(player.age); // Output: 44


In this example, we modify the "age" property of the "player" object by assigning a new value to it. When we directly access the property using dot notation, we can update its value.

Example 2:


const team = {
  name: "Los Angeles Lakers",
  established: 1947,
};

team["name"] = "Golden State Warriors";
console.log(team.name); // Output: Golden State Warriors


Here, we modify the "name" property of the "team" object by using bracket notation. We can access and update the property value by specifying the property name as a string within square brackets.

Example 3:


const player = {
  firstName: "LeBron",
  lastName: "James",
};

Object.assign(player, { age: 38 });
console.log(player.age); // Output: 38


In this example, we use the Object.assign() method to add a new property, "age," to the "player" object. This method takes the target object as the first argument and an object containing properties to be added as the subsequent arguments.

3.2 Adding and Removing Properties Dynamically

JavaScript allows us to dynamically add and remove properties from objects. Let's explore three scenarios:

Example 1:


const player = {
  name: "Valentino Rossi",
};

player.team = "Yamaha";
console.log(player); // Output: { name: "Valentino Rossi", team: "Yamaha" }


In this example, we dynamically add the "team" property to the "player" object by simply assigning it a value. The property is created if it doesn't already exist.

Example 2:


const team = {
  name: "Golden State Warriors",
  established: 1946,
};

delete team.established;
console.log(team); // Output: { name: "Golden State Warriors" }


Here, we use the "delete" keyword to remove the "established" property from the "team" object. The "delete" keyword allows us to remove properties from an object.

Example 3:


const player = {};

Object.defineProperty(player, "name", {
  value: "Michael Jordan",
  writable: false,
});

console.log(player.name); // Output: Michael Jordan
player.name = "Kobe Bryant"; // Error: Cannot assign to read-only property 'name'


In this example, we use the Object.defineProperty() method to add a new property, "name," to the "player" object with specific property attributes. In this case, the "writable" attribute is set to "false," making the property read-only.

3.3 Accessing Nested Object Properties

JavaScript objects can have nested structures with properties inside other objects. Let's see how we can access nested properties with three examples:

Example 1:


const team = {
  name: {
    city: "Los Angeles",
    franchise: "Lakers",
  },
};

console.log(team.name.city); // Output: Los Angeles


In this example, the "team" object has a nested "name" object. We can access the nested "city" property by chaining the property access using dot notation.

Example 2:


const player = {
  details: {
    name: "Stephen Curry",
    team: "Golden State Warriors",
  },
};

const property = "details";
console.log(player[property].name); // Output: Stephen Curry


Here, we demonstrate accessing a nested property using a variable as the key. By using square brackets, we can access the value of the "name" property within the "details" object.

Example 3:


const user = {
  profile: {
    address: {
      city: "New York",
      country: "USA",
    },
  },
};

const { city, country } = user.profile.address;
console.log(city, country); // Output: New York USA


In this example, we use object destructuring to directly extract the nested properties "city" and "country" from the "user" object's "profile" and "address" objects. This syntax allows us to access the nested properties more conveniently.

3.4 Property Descriptors and Property Attributes

In JavaScript, property descriptors define the attributes of an object's properties. Let's explore three important property attributes:

Example 1:


const obj = {};

Object.defineProperty(obj, "property", {
  value: "value",
  writable: true,
  enumerable: true,
  configurable: true,
});

console.log(Object.getOwnPropertyDescriptor(obj, "property"));
/*
Output:
{
  value: "value",
  writable: true,
  enumerable: true,
  configurable: true
}
*/


In this example, we use the Object.defineProperty() method to define a new property, "property," on the "obj" object. We set various attributes such as "value," "writable," "enumerable," and "configurable." The Object.getOwnPropertyDescriptor() method allows us to retrieve the property descriptor.

Example 2:


const player = {};

Object.defineProperty

(player, "name", {
  value: "Valentino Rossi",
  writable: false,
});

player.name = "Marc Marquez"; // Error: Cannot assign to read-only property 'name'




Here, we define the "name" property on the "player" object with the "writable" attribute set to "false." This makes the property read-only, preventing assignment of a new value.

Example 3:


const team = {};

Object.defineProperty(team, "name", {
  value: "Golden State Warriors",
  enumerable: false,
});

for (const key in team) {
  console.log(key); // No output (name property is not enumerable)
}

console.log(Object.keys(team)); // Output: []


In this example, we define the "name" property on the "team" object with the "enumerable" attribute set to "false." As a result, the property is not iterated over in loops and is not included in the keys returned by Object.keys().

Note: Gaining proficiency in modifying object properties, dynamically adding and removing properties, accessing nested object properties, and manipulating property descriptors and attributes will provide you with a strong foundation to effectively work with complex data structures using JavaScript objects.

4. Exploring Object Methods

4.1 Introduction to Object Methods

In JavaScript, object methods are functions that are defined within an object. They allow objects to perform actions or behaviors. Object methods are a powerful way to encapsulate functionality within an object and make the code more modular and organized.

4.2 Creating Methods within Objects

To create a method within an object, you can simply define a function as a property of the object. Let's consider an example:

Example:


const movie = {
  title: 'Inception',
  director: 'Christopher Nolan',
  releaseYear: 2010,
  play: function() {
    console.log('Playing the movie...');
  },
};


In the above code snippet, we have an object called movie with properties such as title, director, and releaseYear. We also define a method called play using the function syntax. This method can be invoked later to start playing the movie.

4.3 Invoking Object Methods and Accessing Object Context

Once an object method is defined, it can be invoked using dot notation or bracket notation, similar to accessing object properties. For example:


movie.play(); // Output: "Playing the movie..."


In this example, we invoke the play method on the movie object using dot notation. The method executes the code inside its function block, which logs the message "Playing the movie..." to the console.

Inside an object method, the this keyword refers to the current object itself. It allows us to access other properties and methods of the object from within the method.

4.4 The "this" Keyword and Its Significance in Object Methods

The this keyword is a special keyword in JavaScript that refers to the context in which a function is executed. In the case of object methods, this points to the object that the method belongs to. Let's see an example:


const series = {
  title: 'Stranger Things',
  cast: ['Millie Bobby Brown', 'Finn Wolfhard', 'Winona Ryder'],
  greet: function() {
    console.log(`Hello, we are the cast of ${this.title}: ${this.cast.join(', ')}.`);
  },
};

series.greet(); // Output: "Hello, we are the cast of Stranger Things: Millie Bobby Brown, Finn Wolfhard, Winona Ryder."


In this code snippet, we have an object called series with properties like title and cast, and a method called greet. Inside the greet method, we use the this keyword to access the title and cast properties of the series object.

The this keyword allows object methods to be more dynamic because it refers to the specific instance of the object that is being used. It enables us to reuse the same method across multiple objects, each with its own set of properties.

Using the this keyword correctly is crucial for object methods to work properly and access the appropriate object context.

Understanding object methods, creating them within objects, invoking them, and grasping the significance of the this keyword allows you to harness the full power of JavaScript objects. This knowledge empowers you to write code that is more modular and organized, enhancing the overall quality of your JavaScript programs.

5. Understanding object prototypes

5.1 Definition of Object Prototypes

JavaScript heavily relies on object prototypes for defining and expanding object behavior. Every object created in JavaScript is associated with a prototype object that serves as a blueprint. Let's explore this concept further with an example:

Example 1: Creating a Scientific Prototype


function Scientist(name, field) {
  this.name = name;
  this.field = field;
}

Scientist.prototype.introduce = function() {
  console.log(`Hello, my name is ${this.name} and I work in the field of ${this.field}.`);
};

const marieCurie = new Scientist('Marie Curie', 'Physics');
marieCurie.introduce(); // Output: Hello, my name is Marie Curie and I work in the field of Physics.


In this example, we define a constructor function Scientist that takes name and field as parameters. The introduce method is added to the Scientist.prototype, which means that all instances of Scientist will have access to this method. When we use the new keyword, we create a new object marieCurie based on the Scientist prototype and call the introduce method on it.

5.2 Creating Objects with Constructor Functions

Constructor functions provide a convenient way to create multiple objects with similar properties and methods. They act as a template to instantiate new objects. Let's see an example:

Example 2: Creating Scientific Objects Using Constructor Functions


function Element(name, symbol, atomicNumber) {
  this.name = name;
  this.symbol = symbol;
  this.atomicNumber = atomicNumber;
}

const hydrogen = new Element('Hydrogen', 'H', 1);
console.log(hydrogen); // Output: Element { name: 'Hydrogen', symbol: 'H', atomicNumber: 1 }


In this example, the Element constructor function defines the properties name, symbol, and atomicNumber. When we create a new instance of Element using the new keyword, the this keyword inside the constructor function refers to the newly created object. We assign values to the object's properties using the this keyword, and the object is returned automatically.

5.3 Prototypal Inheritance and the Prototype Chain

Prototypal inheritance is a powerful feature in JavaScript that allows objects to inherit properties and methods from their prototypes. The prototype chain establishes a hierarchical relationship between objects. Let's illustrate this with an example:

Example 3: Prototypal Inheritance and the Prototype Chain


function Organism(name) {
  this.name = name;
}

Organism.prototype.describe = function() {
  console.log(`${this.name} is an organism.`);
};

function Animal(name, species) {
  Organism.call(this, name);
  this.species = species;
}

Animal.prototype = Object.create(Organism.prototype);
Animal.prototype.constructor = Animal;

Animal.prototype.sound = function() {
  console.log(`${this.name} makes a sound.`);
};

const cat = new Animal('Cat', 'Felis catus');
cat.describe(); // Output: Cat is an organism.
cat.sound(); // Output: Cat makes a sound.


In this example, we have two constructor functions: Organism and Animal. The Animal constructor calls the Organism constructor using Organism.call(this, name) to inherit the name property. We then use Object.create(Organism.prototype) to set up the prototype chain, linking the Animal prototype to the Organism prototype. This allows instances of Animal to access methods defined in Organism.prototype, such as describe. Additionally, we define a sound method specifically for Animal instances.

5.4 The Object.create() Method

The Object.create() method is a powerful tool for implementing prototype-based inheritance in JavaScript. It creates a new object with a specified prototype. Let's see how it works:

Example 4: Using Object.create() for Prototype-based Inheritance


const mammal = {
  describe: function() {
    console.log(`${this.name} is a mammal.`);
  }
};

const whale = Object.create(mammal);
whale.name = 'Whale';
whale.sound = function() {
  console.log(`${this.name} makes a whale sound.`);
};

whale.describe(); // Output: Whale is a mammal.
whale.sound(); // Output: Whale makes a whale sound.


In this example, we create a mammal object with a describe method. When we use Object.create(mammal), we create a new object whale that inherits from the mammal object. We then define the name property and the sound method specifically for the whale object. The whale object can access the describe method defined in its prototype (mammal).

Note: Object prototypes, constructor functions, prototypal inheritance, and the Object.create() method are essential concepts in JavaScript that provide powerful tools for creating reusable and extensible code. With a grasp of these concepts, you can efficiently build complex data structures and establish relationships between objects, resulting in flexible code organization.

6. Object Iteration and Manipulation

6.1 Different Techniques for Iterating Over Object Properties

When working with objects in JavaScript, it's indispensable to be able to iterate over their properties effectively. Let's explore three techniques for iterating over object properties:

6.1.1 Using the for...in loop


for (let key in japan) {
  if (japan.hasOwnProperty(key)) {
    // Access the property using japan[key]
  }
}


Explanation: The for...in loop iterates over all enumerable properties of an object, including those inherited from its prototype chain. We use hasOwnProperty() to ensure that only the object's own properties are accessed.

6.1.2 Utilizing Object.keys()


const keys = Object.keys(japan);
keys.forEach((key) => {
  // Access the property using japan[key]
});


Explanation: The Object.keys() method returns an array containing all the enumerable property names of the object. We can then use forEach() to iterate over the keys and access the corresponding properties.

6.1.3 Leveraging Object.getOwnPropertyNames()


const propertyNames = Object.getOwnPropertyNames(japan);
propertyNames.forEach((name) => {
  // Access the property using japan[name]
});


Explanation: Unlike Object.keys(), Object.getOwnPropertyNames() returns an array containing all property names, regardless of their enumerability. It allows us to access both enumerable and non-enumerable properties of an object.

6.2 Object.keys(), Object.values(), and Object.entries() Methods

JavaScript provides three handy methods - Object.keys(), Object.values(), and Object.entries() - to extract specific information from objects.

6.2.1 Object.keys()


const keys = Object.keys(japan);


Explanation: Object.keys() returns an array of the object's enumerable property names. It allows you to quickly retrieve the keys without needing to iterate over the entire object.

6.2.2 Object.values()


const values = Object.values(japan);


Explanation: Object.values() returns an array of the object's enumerable property values. It provides a convenient way to access the values without explicitly iterating over the object.

6.2.3 Object.entries()


const entries = Object.entries(japan);


Explanation: Object.entries() returns an array of arrays, where each inner array contains a key-value pair of the object's enumerable properties. It allows you to access both the keys and values simultaneously.

6.3 Modifying Object Properties During Iteration

Sometimes, while iterating over an object, you may need to modify its properties. However, modifying properties directly within a loop can lead to unexpected behavior. To handle this situation correctly, you can create a copy of the object and modify the copy instead.


const originalObject = { name: 'Tokyo', population: 13929286 };
const modifiedObject = {};

for (let key in originalObject) {
  if (originalObject.hasOwnProperty(key)) {
    modifiedObject[key] = originalObject[key] + '_modified';
  }
}

console.log(modifiedObject);


Explanation: In this example, we iterate over the properties of originalObject and assign modified values to modifiedObject. By creating a separate object, we ensure that the original object is not modified during the iteration.

6.4 Deep Cloning and Shallow Copying of Objects

There are situations where you need to create a copy of an object. Depending on your requirements, you may choose between deep cloning and shallow copying.

6.4.1 Shallow Copying


const shallowCopy = Object.assign({}, originalObject);


Explanation: Shallow copying creates a new object with the same top-level properties and values as the original object. However, if the original object contains nested objects or arrays, they are still shared between the original and copied objects.

6.4.2 Deep Cloning using JSON


const deepClone = JSON.parse(JSON.stringify(originalObject));


Explanation: Deep cloning creates a completely independent copy of the original object, including any nested objects or arrays. It effectively breaks the reference to the original object and ensures that modifications to the clone won't affect the original.

Note: Deep cloning using JSON has limitations and may not work with objects containing functions or cyclic dependencies.

6.4.3 Deep Cloning using External Libraries

To perform deep cloning with more complex objects or overcome the limitations of JSON-based cloning, you can use external libraries such as Lodash or Underscore.js, which provide dedicated methods like cloneDeep().


const deepClone = _.cloneDeep(originalObject); // Using Lodash


Explanation: External libraries like Lodash provide reliable and efficient deep cloning methods that can handle complex objects and edge cases more effectively than JSON-based cloning.

Note: Proficiency in these techniques involving iterating over object properties, employing object methods effectively, and comprehending various object copying approaches establishes a strong groundwork for handling intricate data structures in JavaScript.

7. JSON and Object Serialization

7.1 Introduction to JSON (JavaScript Object Notation)

JSON, short for JavaScript Object Notation, is a lightweight data interchange format that has gained widespread popularity due to its simplicity and compatibility with various programming languages. It provides a standardized way to represent data structures in a human-readable format, making it ideal for data storage, transmission, and interoperability between different systems.

7.2 Converting JavaScript Objects to JSON Strings

In JavaScript, you can convert a JavaScript object into a JSON string using the JSON.stringify() method. This method takes an object as a parameter and returns a JSON string representation of that object.

Example 1: Converting a JavaScript Object to a JSON String


const player = {
  name: "Ichiro Suzuki",
  age: 49,
  team: "Seattle Mariners"
};

const jsonStr = JSON.stringify(player);
console.log(jsonStr);


In this example, we have an object called player with properties such as name, age, and team. When we call JSON.stringify(player), the object is converted into a JSON string representation. The resulting JSON string is then printed to the console using console.log(). The output will be: {"name":"Ichiro Suzuki","age":49,"team":"Seattle Mariners"}.

7.3 Parsing JSON Strings into JavaScript Objects

To convert a JSON string back into a JavaScript object, you can use the JSON.parse() method. This method takes a JSON string as a parameter and returns the corresponding JavaScript object.

Example 2: Parsing a JSON String into a JavaScript Object


const jsonString = '{"name":"Ichiro Suzuki","age":48,"team":"Seattle Mariners"}';

const player = JSON.parse(jsonString);
console.log(player);


In this example, we have a JSON string representation of an object stored in the jsonString variable. By calling JSON.parse(jsonString), the JSON string is parsed and converted back into a JavaScript object. The resulting object is then printed to the console using console.log(). The output will be an object with properties name, age, and team containing the corresponding values.

7.4 Handling Complex Object Structures with JSON

JSON is not limited to simple objects; it can handle complex object structures and nested data. When working with complex object structures, you can nest objects within objects or include arrays of objects.

Example 3: Handling Complex Object Structures with JSON


const team = {
  name: "Japan National Football Team",
  coach: "Hajime Moriyasu",
  players: [
    { name: "Shinji Kagawa", position: "Midfielder" },
    { name: "Yuto Nagatomo", position: "Defender" },
    { name: "Yuya Osako", position: "Forward" }
  ]
};

const jsonStr = JSON.stringify(team);
console.log(jsonStr);


In this example, we have an object called team with properties such as name, coach, and players. The players property is an array of objects representing individual players. By calling JSON.stringify(team), the entire object structure, including the nested objects and array, is converted into a JSON string. The resulting JSON string is then printed to the console. The output will be the JSON representation of the complex object structure.

JSON provides a flexible and efficient way to handle complex object structures, making it a popular choice for data interchange and storage in various applications and APIs.

Note: JSON and its methods for object serialization allow JavaScript developers to seamlessly convert objects into string representations and parse them back into JavaScript objects as required. This capability greatly simplifies data exchange and enhances interoperability between diverse systems.

8. Getters and Setters for Controlling Object Property Access

8.1 Definition of Getters and Setters

Getters and setters are powerful techniques in JavaScript that allow you to control access to object properties. Getters are used to retrieve the value of a property, while setters are used to set or modify the value of a property. Let's explore how they work with some examples:

Example 1:


const country = {
  name: "India",
  capital: "New Delhi",
  
  get info() {
    return `The capital of ${this.name} is ${this.capital}`;
  },

  set info(value) {
    const [name, capital] = value.split(";");
    this.name = name;
    this.capital = capital;
  }
};

console.log(country.info); // Output: "The capital of India is New Delhi"

country.info = "China;Beijing";
console.log(country.name); // Output: "China"
console.log(country.capital); // Output: "Beijing"


In this example, we have an object called country with name and capital properties. We define a getter info that provides information about the country and its capital. We also define a setter info that allows updating both the name and capital at once by splitting a given string.

Example 2:


const language = {
  _name: "Mandarin",

  get name() {
    return this._name;
  },

  set name(value) {
    if (value === "Mandarin" || value === "English") {
      this._name = value;
    } else {
      throw new Error("Invalid language!");
    }
  }
};

language.name = "English";
console.log(language.name); // Output: "English"

language.name = "Spanish"; // Throws an error: "Invalid language!"
console.log(language.name); // Output: "English"


In this example, we have an object called language with a private property _name. We define a getter name that retrieves the name of the language and a setter name that allows setting the name only to "Mandarin" or "English". If an invalid language is provided, an error is thrown, and the language remains unchanged.

8.2 Object.defineProperty() for Fine-Grained Property Control

The Object.defineProperty() method allows fine-grained control over object properties. It enables you to define properties with specific attributes, such as configurability, writability, and enumerability. Let's see how it works:

Example 1:


const city = {};

Object.defineProperty(city, "name", {
  value: "Tokyo",
  writable: false,
  enumerable: true
});

console.log(city.name); // Output: "Tokyo"

city.name = "Osaka"; // Throws an error in strict mode or silently fails in non-strict mode

for (let key in city) {
  console.log(key); // Output: "name"
}


In this example, we use Object.defineProperty() to define the name property on the city object. We set the value to "Tokyo", make it read-only by setting writable to false, and make it enumerable by setting enumerable to true. As a result, the name property cannot be modified, but it can be accessed and iterated over.

Example 2:


const monument = {};

Object.defineProperty(monument, "name", {
  value: "Taj Mahal",
  configurable: false
});

console.log(monument.name); // Output: "Taj Mahal"

delete monument.name; // Throws an error in strict mode or silently fails in non-strict mode

Object.defineProperty(monument, "name", {
  value: "Great Wall of China"
}); // Throws an error in strict mode or silently fails in non-strict mode


In this example, we define the name property on the monument object. We set configurable to false, which prevents the property from being deleted or redefined using Object.defineProperty(). Once defined, the name property becomes immutable and cannot be modified.

8.3 Object.freeze(), Object.seal(), and Object.preventExtensions() for Object Immutability and Protection

JavaScript provides three methods for object immutability and protection: Object.freeze(), Object.seal(), and Object.preventExtensions(). Let's explore each of them with examples:

Example 1:


const city = {
  name: "Seoul",
  population: 10000000
};

Object.freeze(city);

city.population = 9000000; // Silently fails in strict mode or throws an error in non-strict mode

console.log(city.population); // Output: 10000000


In this example, we use Object.freeze() to make the city object immutable. Any attempts to modify its properties will silently fail in strict mode or throw an error in non-strict mode. The population property remains unchanged even though we tried to assign a new value to it.

Example 2:


const country = {
  name: "Japan",
  area: 377972
};

Object.seal(country);

country.area = 400000; // Modifies the area property
country.population = 126500000; // Silently fails in strict mode or throws an error in non-strict mode

console.log(country.area); // Output: 400000
console.log(country.population); // Output: undefined


In this example, we use Object.seal() to seal the country object. The existing properties can still be modified, but no new properties can be added. Therefore, modifying the area property succeeds, but attempting to add the population property fails.

Example 3:


const continent = {
  name: "Asia",
  countries: 48
};

Object.preventExtensions(continent);

continent.timezones = 11; // Silently fails in strict mode or throws an error in non-strict mode

console.log(continent.timezones); // Output: undefined


In this example, we use Object.preventExtensions() to prevent the continent object from having new properties added to it. The attempt to add the timezones property fails, and the property remains undefined.

8.4 Object.assign() for Object Merging and Shallow Copying

The Object.assign() method allows you to merge the properties of multiple objects into a target object. It performs a shallow copy, meaning that nested objects are copied by reference. Let's see how it works:

Example 1:


const target = { a: 1, b: 2 };
const source = { b: 3, c: 4 };

const merged = Object.assign(target, source);

console.log(merged); // Output: { a: 1, b: 3, c: 4 }
console.log(target); // Output: { a: 1, b: 3, c: 4 }
console.log(source); // Output: { b: 3, c: 4 }


In this example, we use Object.assign() to merge the properties of the source object into the target object. The properties of source overwrite the properties with the same names in target. The resulting object, merged, contains the combined properties. The original objects, target and source, are also modified.

Example 2:


const obj1 = { a: 6 };
const obj2 = { b: 7 };
const obj3 = { c: 8 };

const merged = Object.assign({}, obj6, obj7, obj8);

console.log(merged); // Output: { a: 6, b: 7, c: 8 }
console.log(obj1); // Output: { a: 6 }
console.log(obj2); // Output: { b: 7 }
console.log(obj3); // Output: { c: 8 }


In this example, we create a new empty object as the target of the Object.assign() method. This prevents modification of the original objects (obj6, obj7, obj8). The properties from all three objects are merged into the merged object, resulting in a new object with combined properties.

Example 3:


const city1 = { name: "Tokyo" };
const city2 = { name: "Seoul" };

const merged = { ...city1, ...city2 };

console.log(merged); // Output: { name: "Seoul" }
console.log(city1); // Output: { name: "Tokyo" }
console.log(city2); // Output: { name: "Seoul" }


In this example, we have two objects, city1 and city2, both with a property named name. We use the spread syntax (...) to perform object merging. The resulting merged object contains the properties from both objects. Since city2 is spread after city1, its name property overwrites the same property in city1 in the merged result. The original objects, city1 and city2, remain unchanged.

Understanding advanced object techniques in JavaScript such as getters and setters, Object.defineProperty(), object immutability and protection using Object.freeze(), Object.seal(), and Object.preventExtensions(), as well as object merging and shallow copying with Object.assign(), empowers you to control object properties, enhance object behavior, ensure data integrity, and manipulate objects effectively in your JavaScript applications.

9. Exercises: JavaScript Objects (Asia Edition)

Exercise 1:

Create an object called "country" with the following properties:

  • name (string)
  • population (number)
  • capital (string)
  • languages (array)

Add a method called addLanguage to the object that takes a language name as a parameter and adds it to the languages array.

Exercise 2:

Create an object called "food" with the following properties:

  • name (string)
  • cuisine (string)
  • price (number)
  • isSpicy (boolean)

Add a method called isAffordable to the object that returns true if the price of the food is below a certain threshold (e.g., 50), and false otherwise.

Exercise 3:

Create an object called "monument" with the following properties:

  • name (string)
  • location (string)
  • yearBuilt (number)

Add a method called getAge to the object that calculates and returns the age of the monument (current year minus yearBuilt).

Exercise 4:

Create an object called "sport" with the following properties:

  • name (string)
  • type (string)
  • players (number)

Add a method called isTeamSport to the object that returns true if the sport is a team sport (e.g., football), and false if it is an individual sport (e.g., tennis).

Exercise 5:

Create an object called "city" with the following properties:

  • name (string)
  • population (number)
  • area (number)

Add a method called calculatePopulationDensity to the object that calculates and returns the population density of the city (population divided by area).

10. Solutions: JavaScript Objects (Asia Edition)

Exercise 1:

const country = {
  name: "Japan",
  population: 126500000,
  capital: "Tokyo",
  languages: ["Japanese"],
  
  addLanguage(language) {
    this.languages.push(language);
  }
};

country.addLanguage("English");

console.log(country.languages);


Explanation: In this exercise, we create an object called "country" with properties such as "name", "population", "capital", and "languages". We also add a method called "addLanguage" to the object, which takes a language name as a parameter and adds it to the "languages" array. In the example, we invoke the "addLanguage" method to add the language "English" to the "languages" array. Finally, we log the updated "languages" array to the console.

Exercise 2:

const food = {
  name: "Sushi",
  cuisine: "Japanese",
  price: 30,
  isSpicy: false,

  isAffordable() {
    return this.price < 50;
  }
};

console.log(food.isAffordable());


Explanation: In this exercise, we define an object called "food" with properties such as "name", "cuisine", "price", and "isSpicy". We also add a method called "isAffordable" to the object, which checks if the price of the food is below a certain threshold (in this case, 50) and returns true or false accordingly. In the example, we call the "isAffordable" method and log the result to the console.

Exercise 3:

const monument = {
  name: "Taj Mahal",
  location: "Agra, India",
  yearBuilt: 1643,

  getAge() {
    const currentYear = new Date().getFullYear();
    return currentYear - this.yearBuilt;
  }
};

console.log(monument.getAge());


Explanation: In this exercise, we create an object called "monument" with properties such as "name", "location", and "yearBuilt". We also define a method called "getAge" that calculates and returns the age of the monument by subtracting the "yearBuilt" from the current year. In the example, we invoke the "getAge" method and log the result to the console.

Exercise 4:

const sport = {
  name: "Football",
  type: "Team",
  players: 11,

  isTeamSport() {
    return this.type === "Team";
  }
};

console.log(sport.isTeamSport());


Explanation: In this exercise, we define an object called "sport" with properties such as "name", "type", and "players". We also add a method called "isTeamSport" that checks if the sport is a team sport by comparing the value of the "type" property with the string "Team" and returns true or false accordingly. In the example, we call the "isTeamSport" method and log the result to the console.

Exercise 5:

const city = {
  name: "Tokyo",
  population: 13929286,
  area: 2190,

  calculatePopulationDensity() {
    return this.population / this.area;
  }
};

console.log(city.calculatePopulationDensity());


Explanation: In this exercise, we create an object called "city" with properties such as "name", "population", and "area". We also define a method called "calculatePopulationDensity" that calculates and returns the population density of the city by dividing the population by the area. In the example, we invoke the "calculatePopulationDensity" method and log the result to the console.

11. Encouragement

Thank you for investing your time in reading this guide! I trust that you have found it informative and beneficial in enhancing your comprehension of JavaScript JavaScript Objects.

Ultimately, I strongly urge you to continue your learning journey by delving into the next guide [JavaScript variable visibility: demystifying scope and closures for a comprehensive understanding]. Thank you once more, and I look forward to meeting you in the next guide

Comments