Javascript Object Literals
Published: Dec 31, 2023
Objects at a Glance
Objects in JavaScript allow us to model real-world things with code.
Introduction to Objects
Objects are collections of related data and functionality. Think of objects as containers that have properties and methods.
For example, we could create an object to represent a person with properties like name
, age
, and address
and methods like walk()
and talk()
.
Key-Value Pairs
Objects contain key-value pairs. The keys (name, age, etc) are strings that act as identifiers for the values. Values can be any type in JavaScript - strings, numbers, booleans, arrays, functions, or other objects.
const person = {
name: "John",
age: 30
};
Here “name” and “age” are keys, while “John” and 30 are the values.
Object Literal Notation
What it is:
- It’s a concise way to create objects in JavaScript, directly defining their properties and values within curly braces
{}
. - It offers a more readable and maintainable approach compared to using the new
Object()
constructor.
Syntax:
const objectName = {
property1: value1,
property2: value2,
// ... more properties
};
Key elements:
- Curly braces
{}
: Enclose the object’s properties and values. - Property names: Strings that represent the names of the object’s properties.
- Colon
:
: Separates property names from their corresponding values. - Commas
,
: Separate multiple property-value pairs (except after the last pair). - Values: Can be any valid JavaScript data type, including strings, numbers, booleans, arrays, other objects, or functions.
Example:
const person = {
name: "John Doe",
age: 30,
city: "New York",
hobbies: ["reading", "coding", "hiking"],
greet: function() {
console.log("Hello, my name is " + this.name);
}
};
Key features:
- Dynamic property creation: Add or modify properties after object creation:
person.job = "Software Engineer";
- Computed property names: Use expressions for property names (enclosed in square brackets):
const key = "address";
const person = {
[key]: "123 Main St"
};
- Shorthand property names: If property name matches variable name, omit it:
const firstName = "John";
const person = {
firstName, // Equivalent to firstName: firstName
lastName: "Doe"
};
- Methods: Define functions as properties:
const person = {
firstName: "John",
lastName: "Doe",
fullName() {
return this.firstName + " " + this.lastName;
}
};
Benefits:
- Conciseness and readability: Makes code easier to write and understand.
- Maintainability: Properties are clearly defined and organized.
- Flexibility: Dynamically add, modify, or remove properties.
- Functionality: Encapsulate data and behavior within objects.
Creating an Object Literal
Let’s look at how to create a basic object literal in JavaScript.
Basic Syntax
The basic syntax for creating an object literal is:
const objectName = {
key1: value1,
key2: value2
};
Replace objectName
with the name for your object. Add key-value pairs inside the curly braces {}
.
Property Assignment
We can assign values to keys in a few different ways:
const person = {
name: "John",
age: 30
};
// Same as above
const person = {
"name": "John",
"age": 30
};
// Using variables
const nameKey = "name";
const ageKey = "age";
const person = {
[nameKey]: "John",
[ageKey]: 30
};
In the first example, we assign values using key: value
syntax. The property name does not need quotes unless it has spaces or dashes.
The second does the same but with quotes around the keys. This is also valid but not required if it’s a valid identifier.
The third example uses variables for the keys. This can be useful to dynamically create keys. The square bracket syntax is required here.
Adding Methods
Methods are functions that are properties of an object. When a function is declared using a declaration keyword like ‘function’, it becomes a method of the global object. In the case of browsers, the global object is ‘window’. This means that functions declared in the global scope without being explicitly attached to another object are, by default, methods of the ‘window’ object in a browser environment. Here is how to add methods to JavaScript objects.
Functions as Object Properties
We can assign function expressions as values to object properties to add methods:
const person = {
name: "John",
sayHello: function() {
console.log("Hello!");
}
}
person.sayHello(); // Logs "Hello!"
The sayHello
method can be invoked using person.sayHello()
.
Invoking Object Methods
To call an object method, use:
objectName.methodName();
Where objectName
is the name of your object, and methodName
is the method you want to invoke.
Methods have access to other properties on the same object using this
(more on that below)!
Best Practices for Method Definition
Some best practices for methods include:
- Avoid using arrow functions which lexically bind
this
. Use regular functions instead. - Use ES6 method shorthand syntax:
sayHi() {
// method code
}
\
‘this’ Keyword
The special this
keyword references the current object but can sometimes be confusing.
Understanding ‘this’ in Objects
In object methods, this
refers to the object itself:
const person = {
name: "John",
sayName() {
console.log(this); // person object
}
};
person.sayName();
Whereas in regular functions, this
refers to the global object (window
in browsers).
‘this’ in Regular Functions vs. Arrow Functions
Arrow functions lexically bind this
so they inherit this
from the surrounding scope:
const person = {
name: "John",
sayName: () => {
console.log(this); // window
}
};
person.sayName();
So arrow functions should be avoided as object methods.
Common ‘this’ Pitfalls and Solutions
Here are some common this
pitfalls and how to avoid them:
- Losing
this
context: storethis
in a variable - Using arrow functions: stick to regular functions for methods
- Changing
this
(call/apply): leverage lexicalthis
with arrow functions
Objects in Arrays
We can store objects in arrays to organize related data.
Storing Objects in Arrays
An array stores an ordered collection of items. We can store objects inside arrays like so:
const todos = [
{
id: 1,
text: "Take out trash",
completed: false
},
{
id: 2,
text: "Doctor's appointment",
completed: true
}
];
This todos
array contains two todo objects representing tasks.
Iterating Over Object Arrays
We can use loops to iterate over arrays of objects:
for (let i = 0; i < todos.length; i++) {
console.log(todos[i].text); // Log text property
}
We can access object properties inside the loop on each iteration.
The forEach
array method is also useful for looping over object arrays.
Use Cases for Objects in Arrays
Some examples of storing objects in arrays include:
- User objects stored in an array as a simple database
- Todo/task objects for a todo list web app
- Product objects in a shopping cart array
This is a very common pattern in JavaScript apps.
Math Object
The built-in Math object has useful methods and properties for mathematical operations.
Overview of the Math Object
- Static properties and methods (not constructable with
new
) - Useful for common math tasks in programming
- Helps avoid issues with numbers in JS
Math.PI; // 3.14159...
Math.floor(3.14); // 3
Math.ceil(3.14); // 4
Math.min(1,3,2); // 1
Math.random(); // Random number
Common Math Operations
Here are some common math operations with Math
:
Math.round()
,Math.floor()
,Math.ceil()
- round to integerMath.min()/Math.max()
- find min/max valueMath.random()
- generate random number- Many more!
Math Object Use Cases
Some example use cases:
- Generate random ID numbers for objects
- Calculate distances/physics in games
- Formulas/operations across app
The Math
object helps avoid issues with representing numbers in JS and supports many common math functions out of the box.
Primitive vs. Reference Types
In JavaScript, data types can be either primitive or reference types. This affects how they are stored and interacted with.
Understanding Primitive Types
Primitive types include:
- Strings
- Numbers
- Booleans
- null and undefined
Primitives store only a single value directly in the variable itself.
For example:
let name = "John";
The string “John” is stored directly in the name
variable.
Understanding Reference Types
Reference types include:
- Arrays
- Functions
- Objects
References don’t store the value directly. They point to a location in memory where the object is stored.
For example:
const person = {
name: "John"
};
person
doesn’t contain the object directly. It points to a memory location storing the object data.
Implications in Object Literals
In practice, this means:
- Primitives are copied by value
- Objects are copied by reference
This becomes very relevant when passing functions arguments or assigning objects to new variables.
let name = "John";
let name2 = name;
name2 = "Jane";
console.log(name); // "John"
Here the string is copied by value - name2
gets a copy of the value in name
.
Objects behave differently:
let person = {
name: "John"
};
let person2 = person;
person2.name = "Jane";
console.log(person.name); // "Jane"
person
and person2
point to the same object location. Changing one also affects the other!
To truly copy objects, we need to manually create a copy with destructuring or Object.assign()
:
// Destructuring copy
let {name: name2} = person;
name2 = "Jane";
console.log(person.name); // "John"
So because objects are stored by reference, modifications can unexpectedly change variables. Destructuring allows true copies of object data.
Object Destructuring
Destructuring allows us to extract data from objects and arrays into distinct variables.
Extracting Values from Objects
With object destructuring we can extract values like this:
const person = {
firstName: "John",
lastName: "Doe",
age: 32
}
// Object destructuring
const {firstName, age} = person;
console.log(firstName); // "John"
console.log(age); // 32
This pulls properties out into separate variables.
Simplifying Code with Object Destructuring
Destructuring objects can simplify code by reducing redundant access:
// Without destructuring
function printDetails(user) {
console.log(user.firstName, user.lastName);
}
// With destructuring
function printDetails({firstName, lastName}) {
console.log(firstName, lastName);
}
By destructuring in the function argument, we skip redundant user.
references.
Use Cases for Object Destructuring
Object destructuring shines anytime we need to access multiple properties of an object. Some examples:
- Initializing variables
- Function arguments
- Pulling API response data
Object Spread Operator
The spread syntax (…) allows us to expand objects into other objects very useful for combining data.
Introduction to the Spread Operator
The spread operator unpacks elements:
const numbers = [1,2,3];
Math.max(...numbers);
We spread an iterable like an array into individual arguments.
Copying Objects with Spread
We can create shallow copies of objects with the spread syntax:
const person = {
name: "John",
age: 30
}
const copiedPerson = {...person};
This quickly copies all key-value pairs into a new object.
Merging Objects Using Spread
The spread operator can also merge objects together:
const person = {
name: "John"
};
const job = {
role: "Teacher"
}
// Merge objects
const employee = {...person, ...job};
console.log(employee);
// {name: "John", role: "Teacher"}
This allows easily combining data from multiple sources.
Object Methods & Properties
JavaScript objects come with useful built-in methods we can use.
Common methods include:
Object.keys()
– Returns array of keysObject.values()
– Returns array of valuesObject.entries()
– Returns keys & values
Consider this object:
const person = {
name:"John",
age: 30
};
Object.keys(person); // ["name", "age"]
Object.values(person); // ["John", 30]
Object.entries(person); // [["name", "John"], ["age", 30]]
These provide different views into the object data.
Iterating Over Object Properties
We can also use these methods to iterate (loop) over objects:
// Loop over keys
for (let key of Object.keys(person)) {
console.log(key); // Prints name, age
}
They enable easily looping without having to manually access properties.
Use Cases for Object Methods
Here are some example use cases for these methods:
- Convert objects into maps/arrays
- Iterate over objects in loops
- Access all values or keys easily
Immutability in Objects
Immutable objects cannot be changed after creation. This provides several benefits.
Understanding Immutability
An immutable object can’t be modified after it’s created. Consider this:
const person = {
name: 'John'
};
person.name = 'Jane'; // Can be modified!
This person
object is mutable - it can be changed after creation.
An immutable object could not have its properties changed like this.
Techniques for Achieving Immutability
There are a few ways to achieve immutability in JavaScript: Const
Declaring an object as const
only prevents reassignment of the variable. The properties can still change:
const person = {
name: 'John'
};
person.name = 'Jane'; // Allowed!
Object.freeze()
The Object.freeze()
method prevents new properties from being added and existing properties changed:
const person = {
name: 'John'
};
Object.freeze(person);
person.name = 'Jane'; // Throws an error
Now the object is immutable!
Spread operator
We can use the spread syntax to create a new copy whenever an object changes:
let person = {...originalPerson};
person = {...person, name: 'Jane'}; // Create updated copy
This ensures modifications create a new object instead.
Benefits of Immutable Objects
Benefits include:
- Avoid unexpected side effects from modifying objects
- Help manage shared data
- Enable useful patterns like redux Overall, immutability leads to safer and more predictable code.
Object Prototypes
When you create an object in JavaScript, that object is linked to another object called its “prototype”. This prototype linkage allows objects to inherit features from other objects.
The Prototype Chain
All JavaScript objects have a private [[Prototype]]
property that’s either another object or null
. When you try to access a property on an object:
- JavaScript first looks for the property on the object itself
- If it’s not found, it then searches the object’s prototype object,
- And keeps going up the prototype chain until the property is found or it reaches
null
. This is the prototype chain.
So the prototype acts as a fallback for properties that don’t exist on the object itself.
Inheriting from Object.prototype
By default, all custom objects inherit properties and methods from the base Object.prototype
:
const user = { name: "John" };
user.toString(); // Works even though we didn't define it
Here user
inherits the toString()
method from Object.prototype
.
We can access an object’s prototype using Object.getPrototypeOf()
:
Object.getPrototypeOf(user); // Object.prototype
Setting the Prototype
Every function has a prototype
property that allows setting the prototype for objects created with that function:
function User(name) {
this.name = name;
}
User.prototype.sayHi = function() {
alert(`Hi, I am ${this.name}`);
}
const me = new User("John");
me.sayHi(); // Works!
Now User
objects inherit from the custom User.prototype
object.
Prototypes allow objects to share and extend functionality in a dynamic, modular way. It’s a key aspect of OOP in JavaScript.
Common Mistakes and Best Practices
When working with object literals, there are some best practices we can follow to write optimized, maintainable code.
Avoiding Common Pitfalls in Object Literal Usage
Here are some common mistakes to avoid: Modifying object literals incorrectly
const person = {
name: "John"
};
person = {}; // Re-assigning loses reference
person.name = "Jane"; // Modifying non-existent property
Don’t re-assign the object variable or try to change properties on an undefined object.
Using reserved words as keys
const person = {
class: "Computer Science" // Causes syntax error
};
Avoid using reserved words and symbols as key names.
Mixing up declaring and invoking
let user = {
name: "John",
print: printDetails(); // Runs instantly!
}
function printDetails() {
// Prints undefined
// Because print() already ran above
}
Don’t invoke functions in objects, simply refer to them.
Best Practices for Object-oriented JavaScript
Some best practices include:
- Use descriptive key names
- Create objects with same properties grouped
- Store methods on prototypes instead of instances
This leads to clean, readable code.
Writing Maintainable and Readable Object Literals
Some tips for writing good object literals:
- Be consistent and predictable in naming
- Use comments for complex object literals
- Format code neatly into semantic blocks
Your future self and other developers will thank you!
Conclusion
Object literals are the bread and butter of working with data and config in JavaScript. By following best practices of descriptive declaration, correct method assignment, reusing prototypes, immutable data and prudent manipulation, we build robust, maintainable and performant code. Becoming an expert in literal JavaScript objects pays huge dividends towards mastering the language paradigms.
Help Improve This Page
Spot an error or have a suggestion? This page is a Markdown file, making it easy to contribute:
- Click the "Edit on GitHub" button below
- Look for the pencil icon in the GitHub interface
- Make your changes and submit a pull request
Can't edit directly? Share your feedback or report issues in the comments below. We appreciate your input!