Understanding JavaScript Objects: A Comprehensive Guide

Understanding JavaScript Objects: A Comprehensive Guide

Welcome to Day 7 of our 30-day JavaScript and Node.js learning seriesIn the previous article, we introduced you to the JavaScript Arrays, and today, we’ll dive deeper into one of the most crucial topics—JavaScript Objects.

Objects are a fundamental data type in JavaScript, serving as building blocks for creating complex applications. Essentially, they are collections of key-value pairs, where keys are known as properties and values can be of any data type. Therefore, understanding objects is crucial for mastering JavaScript programming.

In this comprehensive guide, we’ll delve into the intricacies of objects. To begin with, we’ll explore how to create, access, and modify them. Additionally, we’ll learn how to use them effectively.

Creating Objects

First and foremost, we create objects using various methods in JavaScript. The simplest method is the object literal, where we define an object by enclosing key-value pairs within curly braces. For instance, you can create an object with a few properties by creating an instance of object directly. On the other hand, another common way to create objects is by using a constructor function.

  1. Object Literal Notation:
const person = {
    firstName: "John",
    lastName: "Doe",
    age: 30,
    city: "New York",
    greet: function() {
        console.log("Hello, my name is " + this.firstName + " " + this.lastName);
    }
};

2. By Creating an Instance of Object Directly

const person = new Object();
person.firstName = "John";
person.lastName = "Doe";
person.age = 30;
person.city = "New York";

3. Constructor Functions:

function Person(firstName, lastName, age, city) {
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
this.city = city;
this.greet = function() {
console.log("Hello, my name is " + this.firstName + " " + this.lastName);
};
}

const person1 = new Person("Alice", "Smith", 25, "Los Angeles");

In addition, JavaScript provides the Object.create() method, which creates a new object based on a specified prototype. This method is particularly useful when you want to create an object that inherits properties from another object.

Choosing the Right Method

  • Object Literal Notation is ideal for creating simple objects with a fixed structure.
  • Constructor Functions, on the other hand, are better suited for creating more complex objects with multiple instances and inheritance.

Accessing and modifying Properties

Next, let’s look at how to access and modify object properties. You can retrieve a property using dot notation, like person.name, or bracket notation, such as person["name"]. However, if the property you are accessing does not exist, it will return undefined.

console.log(person.firstName); // Output: John
console.log(person["lastName"]); // Output: Doe

Moreover, you can modify existing properties by simply assigning a new value to them:

person.occupation = "Developer";

Afterwards, if you need to add new properties, JavaScript allows you to do so dynamically. For example, you can add an address property:

person.age = 31;

In addition to this, you can delete properties using the delete keyword:

delete person.city;

Object Methods

Objects can have methods, which are functions defined within the object itself. Methods are used to perform actions on the object’s data.

Built-in Methods

Furthermore, JavaScript objects come with a variety of built-in methods to help with object manipulation.

Object Manipulation Methods:

  • Object.assign(target, ...sources):
    • Copies own enumerable properties from one or more source objects to a target object.
    • It is important to note does not copy non-enumerable properties (properties set with enumerable: false using Object.defineProperty).
    • Overwrites existing properties in the target object with properties from sources, based on property names.
    • Returns the modified target object.
    • Example:
const person = { name: "John" };
const details = { age: 30, city: "New York" };
const combined = Object.assign(person, details); // combined: { name: "John", age: 30, city: "New York" }
  • Object.create(prototype[, propertiesObject]):
    • Creates a new object that uses the specified prototype object.
    • The prototype object is used for property lookup when properties are accessed on the newly created object.
    • Optionally, you can provide a second argument, propertiesObject, to define specific properties on the new object.
    • Example:
function Person(name) { this.name = name; }
const person1 = new Person("Alice"); // person1 inherits from Person.prototype
const person2 = Object.create(Person.prototype, { age: { value: 25, writable: true } }); // person2 inherits from Person.prototype with a custom "age" property

Property Definition and Access Methods:

Property Definition
  • Object.defineProperty(obj, prop, descriptor):
    • Defines a new property directly on an object (obj), or modifies an existing property.
    • Takes three arguments:
      • obj: The object to define the property on.
      • prop: The name of the property (string or symbol).
      • descriptor: An object that describes the property (with properties like valuewritableenumerableconfigurable).
    • Example:
const person = {};
Object.defineProperty(person, "name", { value: "John", writable: true, enumerable: true });
console.log(person.name); // Output: John
  • Object.defineProperties(obj, propertiesObject):
    • Defines multiple properties on an object at once.
    • Takes two arguments:
      • obj: The object to define the properties on.
      • propertiesObject: An object whose properties will be defined on the target object.
    • Example:
const person = {};
Object.defineProperties(person, {
  name: { value: "John", writable: true },
  age: { value: 30, writable: false }
});
console.log(person.name); // Output: John
person.age = 31; // Throws an error because "age" is not writable
  • Object.freeze(obj):
    • Prevents modifications to an object’s existing properties (value, writable, configurable).
    • Attempts to modify a frozen object’s properties will throw a TypeError.
    • Frozen objects cannot be further modified.
    • Example:
const person = { name: "John" };
Object.freeze(person);
person.name = "Alice"; // Throws a TypeError because the object is frozen
  • Object.preventExtensions(obj):
    • Prevents new properties from being added to an object.
    • Existing properties can still be modified.
    • Example:
const person = {};
Object.preventExtensions(person);
person.city = "New York"; // Throws a TypeError
  • Object.seal(obj):
    • It prevents both new properties from being added and makes existing properties non-configurable.
    • Similar to Object.freeze but allows existing properties to be modified.
    • Example:
const person = {
    name: "Clara"
};
Object.seal(person);
person.city = "New York"; // Throws a TypeError
person.name = "Alice"; // Allowed (existing property can be modified)
delete person.city; // Throws a TypeError
Access Methods:
  • Object.entries(obj):
  • Returns an array of key-value pairs from an object’s own enumerable properties.
  • Each element in the array is an array of [key, value].
  • Example:
const person = { name: "John", age: 30 };
const entries = Object.entries(person); // entries: [["name", "John"], ["age", 30]]
  • Object.getOwnPropertyDescriptor(obj, prop):
    • Returns a property descriptor object for a specified property (prop) on an object (obj).
    • The descriptor object describes the property’s characteristics (value, writable, enumerable, configurable).
    • Example:
const person = { name: "John" };
const descriptor = Object.getOwnPropertyDescriptor(person, "name");
console.log(descriptor); // { value: "John", writable: true, enumerable: true, configurable: true }
  • Object.getOwnPropertyDescriptors(obj):
    • Returns an object containing all own property descriptors for an object.
    • Similar to Object.getOwnPropertyDescriptor but returns all properties at once.
    • Example:
const person = { name: "John", age: 30 };
const descriptors = Object.getOwnPropertyDescriptors(person);
console.log(descriptors); // { name: { ... }, age: { ... } } (details of each property descriptor)
  • Object.getOwnPropertyNames(obj):
    • Returns an array of all own property names (strings) of an object.
    • Only enumerable properties are included.
    • Example:
const person = { name: "John", age: 30, _secret: "" }; // _secret is not enumerable by default
const propertyNames = Object.getOwnPropertyNames(person); // propertyNames: ["name", "age"]
  • Object.getOwnPropertySymbols(obj):
    • Returns an array of all own property names (symbols) of an object.
    • Only enumerable properties are included.
    • Example:
const person = { name: "John", age: 30, [Symbol.for("secret")]: "" }; // Symbol property
const propertySymbols = Object.getOwnPropertySymbols(person); // propertySymbols: [Symbol(secret)]
  • Object.getPrototypeOf(obj):
    • Returns the object’s prototype (the object that the current object inherits from).
    • If the object has no prototype (e.g., Object.create(null)), it returns null.
    • Example:
function Person() {}
const person1 = new Person();
console.log(Object.getPrototypeOf(person1)); // Output: Person.prototype
  • Object.is(value1, value2):
    • Determines if two values are exactly the same.
    • Handles special cases like NaN0, and -0 more reliably than ==.Example
  • Object.isExtensible(obj):
    • Checks if an object can be extended (new properties can be added).
    • Returns true if the object is extensible, false otherwise.
    • Example:
const person = {};
console.log(Object.isExtensible(person)); // Output: true
Object.preventExtensions(person);
console.log(Object.isExtensible(person)); // Output: false
  • Object.isFrozen(obj):
    • Checks if an object is frozen (no modifications are allowed).
    • Returns true if the object is frozen, false otherwise.
    • Example:
const person = {};
Object.freeze(person);
console.log(Object.isFrozen(person)); // Output: true
  • Object.isSealed(obj):
    • It checks if an object is sealed, meaning no new properties can be added, although existing properties can still be modified.
    • Returns true if the object is sealed, false otherwise.
    • Example:
const person = {};
Object.seal(person);
console.log(Object.isSealed(person)); // Output: true
  • Object.keys(obj):
    • Returns an array of an object’s own enumerable property names (strings).
    • Example:
const person = { name: "John", age: 30 };
const keys = Object.keys(person); // keys: ["name", "age"]
  • Object.setPrototypeOf(obj, proto):
    • Sets the prototype of an object.
    • Replaces the existing prototype with the specified proto object.
    • Example:
const person = {};
const personPrototype = { greet: function() { console.log("Hello!"); } };
Object.setPrototypeOf(person, personPrototype);
person.greet(); // Output: Hello!
  • Object.values(obj):
    • Returns an array of an object’s own enumerable property values.
    • Example:
const person = { name: "John", age: 30 };
const values = Object.values(person); // values: ["John", 30]

Custom Methods

Additionally, you can also define your own custom methods on objects:

const person = {
    firstName: "John",
    lastName: "Doe",
    age: 30,
    greet: function() {
        console.log("Hello, my name is " + this.firstName + " " + this.lastName);
    },
    getFullName: function() {
        return this.firstName + " " + this.lastName;
    }
};

Nested Objects

Objects can be nested within other objects. For example, consider the following:

const address = {
    street: "123 Main St",
    city: "New York",
    zipCode: "10001"
};

const person = {
    firstName: "John",
    lastName: "Doe",
    age: 30,
    address: address
};

Arrays as Properties

Objects can have arrays as properties:

const person = {
    firstName: "John",
    lastName: "Doe",
    hobbies: ["reading", "hiking", "programming"]
};

Object Prototypes

Objects in JavaScript inherit properties and methods from their prototype. Thus, the prototype chain allows objects to access properties and methods defined on their prototype and parent prototypes.

function Person(firstName, lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
}

Person.prototype.greet = function() {
    console.log("Hello, my name is " + this.firstName + " " + this.lastName);
};

const person1 = new Person("Alice", "Smith");
person1.greet(); // Output: Hello, my name is Alice Smith

When to Use Prototypes

Prototypes are useful for creating reusable code and implementing inheritance. In fact, they can be especially helpful when you need to define common properties and methods for multiple objects.

Object Copying and Cloning

When you assign one object to another, both variables will reference the same object in memory. As a result, one object will reflect any changes made to the other.

To create a true copy of an object, you can use the Object.assign() method or the spread operator:

const person = {
    firstName: "John",
    lastName: "Doe",
    age: 30
};

// Using Object.assign()
const personCopy = Object.assign({}, person);

// Using the spread operator
const personCopy2 = { ...person };

Now, personCopy and personCopy2 are independent copies of the original person object. Changes made to one copy will not affect the other.

Conclusion

To summarize, JavaScript objects are a powerful feature that allows developers to structure and manage data efficiently. Not only do they provide flexibility through dynamic properties, but their built-in methods also make manipulation and iteration easy. In addition to these capabilities, understanding how to effectively use objects will help you build more organized and maintainable code in your JavaScript projects.

In our next post we will explore ES6+ Features in JavaScript.


Previous Lesson

Day 6: Javascript Arrays

Next Lesson

Day 8: ES6+ Features in JavaScript


Share with your friends


3 Comments

No comments yet. Why don’t you start the discussion?

    Leave a Reply

    Your email address will not be published. Required fields are marked *