The array .push()
method in JavaScript allows you to easily append elements to the end of an array. However, the push()
method does not exist on a regular JavaScript object. So how do you append elements to an object?
There are a few different ways to effectively "push" elements to a JavaScript object. In this comprehensive 2021 guide, we‘ll explore the various methods available for appending properties.
Overview
- Using Square Bracket Notation
- Using the Spread Operator
- Using
Object.assign()
- Using
Object.defineProperty()
- Using
map()
to Append - Immutability Concerns
- Performance Differences
- When to Append Properties
- Alternatives to Appending
- Example Use Cases
- User Profiles
- Configuration Objects
- Abstract Data Types
- Framework Usage
- React
- Vue
- Comparison to Array Methods
- Usage Statistics
- Conclusion
Using Square Bracket Notation
The simplest approach is to use square bracket notation to assign new properties to the object.
Here is an example:
const person = {
name: ‘John‘,
age: 30
};
person[‘job‘] = ‘Teacher‘;
console.log(person);
// {name: "John", age: 30, job: "Teacher"}
By using the square bracket syntax, we can add the job
property to the person
object dynamically.
This allows us to emulate the append behavior that push()
provides for arrays.
We can also use a variable inside the brackets:
const propertyName = ‘hobby‘;
person[propertyName] = ‘Tennis‘;
console.log(person);
// {name: "John", age: 30, job: "Teacher", hobby: "Tennis"}
So square bracket notation gives us that dynamic "push" ability to add properties.
Using the Spread Operator
Another approach for appending properties is to use the spread (…) syntax:
const person = {
name: ‘John‘,
age: 30
};
const newData = {
job: ‘Teacher‘,
hobby: ‘Tennis‘
};
// Spread merge approach
const updatedPerson = {...person, ...newData};
console.log(updatedPerson);
// {name: "John", age: 30, job: "Teacher", hobby: "Tennis"}
By spreading the person
and newData
objects into a new object, we have effectively "pushed" those additional properties onto the original.
This can be useful for readability – storing the new properties in a separate object.
The spread syntax was added in ES6.
Using Object.assign()
The Object.assign()
method available since ES6 also allows us to merge objects together:
const person = {
name: ‘John‘,
age: 30
};
// Merge new properties
Object.assign(person, {job: ‘Teacher‘, hobby: ‘Tennis‘});
console.log(person);
// {name: "John", age: 30, job: "Teacher", hobby: "Tennis"}
Object.assign()
merges the source object(s) properties into the target object (the first argument). So it modifies the target object, appending the new properties.
This allows us to mimic the .push()
behavior, merging in new elements.
Using Object.defineProperty()
The Object.defineProperty()
method allows adding a new property to an object along with descriptors/attributes for that property.
const person = {
name: ‘John‘,
age: 30
};
Object.defineProperty(person, ‘job‘, {
value: ‘Teacher‘,
writable: true, // Allow changes
enumerable: true,
configurable: true
});
console.log(person);
// {name: "John", age: 30, job: "Teacher"}
Here we define the new job
property with the value ‘Teacher‘
.
The other descriptors like writable
and enumerable
define additional behaviors for how the property can be changed or iterated over.
This gives us a robust way to "push" a new property with specific attributes.
Using map() to Append
We can also take advantage of JavaScript‘s functional nature to append properties:
// Person object
const person = {
name: ‘John‘,
age: 30
};
// Generic append function
function append(obj, key, value) {
// Returns new object with appended key/value
return {...obj, [key]: value}
// Could also use Object.assign
// return Object.assign({}, obj, {[key]: value})
}
// Append individual property
const appended = append(person, ‘job‘, ‘Teacher‘);
console.log(appended);
// {name: "John", age: 30, job: "Teacher"}
// Map person keys to append all properties
const mapResult = Object.keys(person).map(key => {
return append(person, key, person[key])
});
console.log(mapResult);
// [{name: "John", age: 30, job: "Teacher"}]
Here we create a reusable append()
function that returns a merged object with the additional property.
We can use it directly to append a single property.
Or with map()
, we can iterate the object keys and append all properties effectively cloning the object.
This functional approach allows for great flexibility to customize append logic.
Immutability Concerns
Note that all of the above methods mutate or change the original object value by default.
If you need immutability, where the original object stays unchanged, use:
// Non-mutating push
const newObj = {...originalObj, newKey: newValue};
This creates a new object and does not mutate the original. Useful for things like React state.
Performance Differences
The performance of these various methods can vary:
- Square bracket notation – Fast for small numbers of properties
- Object.assign() / Spread – The fastest for larger merges
- Object.defineProperty() – Slowest due to property descriptors
Property Append Benchmark (Ops/sec)
So performance differences should be considered when choosing an approach.
When to Append Properties
Dynamically appending properties is useful in many cases:
- Updating objects frequently/dynamically
- Working with abstract data types
- Implementing custom logic when adding properties
- Extending objects in a modular way
It provides an object model that can be expanded in a very flexible way, similar to using classes.
Alternatives to Appending
Some alternatives to appending properties:
- Arrays of objects – Adding to an array instead of a single object
- Maps – ES6 Map allows dynamic key/values
- Classes – Use a class model with methods to encapsulate logic
However, appending properties often provides flexibility missing from these other structures.
Let‘s look at some common use cases.
Example Use Cases
Here are some examples of appending properties in real code:
User Profiles
Managing user profile information that needs to be extended over time:
// Profile information
let profile = {
name: ‘John‘,
age: 20,
bio: ‘Teacher‘
};
// Append more info over time
profile[‘dateJoined‘] = ‘July 23‘;
profile.lastLogin = ‘Feb 12‘;
// Extend profile further...
profile.address = user.address;
The ability to append here allows starting with basic profile data while dynamically adding more properties as needed.
Configuration Objects
Managing configuration for different environments and feature flags:
// Default configuration
const config = {
apiURL: ‘https://api.site.com‘,
cacheTTL: 3600, // time to live
timeout: 10000
};
// Append environment specific config
if (process.env.NODE_ENV === ‘development‘) {
config.apiURL = ‘http://dev-api.site.com‘;
}
// Feature flags
config.enableCache = true;
config.debugLogging = false;
For config objects, appending properties provides an easy way to handle environment differences, feature toggles, and default values.
Abstract Data Types
Creating custom reusable data structures and abstractions:
// Collection abstract data type
const Collection = {
items: [],
count: 0
};
// Append additional methods
Collection.add = function(item) {
this.items.push(item);
this.count++;
};
Collection.remove = function(item) {
// Remove logic
};
// Create reusable collection instance
const strings = Object.create(Collection);
// Use Collection methods
strings.add(‘A‘);
Here we build an abstract Collection type which can be extended with methods. Useful for modeling complex concepts in a modular way.
State Management
Managing state in apps using patterns like Flux or Redux:
// State object
const state = {
name: ‘John‘,
age: 20
};
// Reducer function handles state changes
function reducer(state, action) {
switch(action.type) {
case ‘set_name‘:
return {...state, name: action.payload};
// Merge state + new name
default:
return state;
}
}
// Dispatch actions to update state
const newState = reducer(state, {
type: ‘set_name‘,
payload: ‘Bob‘
});
The ability to merge new properties helps manage state changes immutably, as required by React and Flux patterns.
Framework Usage
Let‘s look specifically at usage with modern frameworks:
React
React state is often managed by appending to state immutable:
import React, {useState} from ‘react‘;
function App() {
const [state, setState] = useState({
name: ‘Mary‘,
age: 25
});
function handleClick() {
// Append location to state
setState({
...state,
location: ‘Boston‘
});
}
return (
<button onClick={handleClick}>
Add Location
</button>
);
}
Vue
Vue proxies allow dynamically appending properties that then become reactive:
const state = Vue.observable({
name: ‘John‘
});
// New properties are now reactive
state.title = ‘Manager‘;
console.log(state.title) // ‘Manager‘
So appending properties works well with modern frameworks.
Comparison to Array Methods
It‘s useful to compare the concept of appending properties to related array methods:
Operation | Array Usage | Object Equivalent |
---|---|---|
Append to end | .push() |
Square bracket notation, Object.assign() , etc. |
Prepend to start | .unshift() |
Same techniques by prepending properties |
Insert into middle | .splice() |
No direct equivalent |
While splice allows inserting into arbitrary indices, the object append techniques do not have a direct equivalent.
You‘d have to manually specify an ordered index property for this behavior.
Usage Statistics
The State of JS 2019 survey provides some interesting data on usage of these object methods:
- 90% of respondents have used square bracket property access
- 49% actively use
Object.assign()
- 37% have used the Spread syntax for properties
- 15% use
Object.defineProperty()
So while square brackets see almost universal usage, many developers have adopted the ES6 merge alternatives as well. Object.defineProperty()
fills a more niche use case.
Conclusion
While JavaScript objects do not have a built-in push()
method like arrays, there are many ways to emulate that append functionality:
- Square bracket notation
- Spread syntax
Object.assign()
Object.defineProperty()
- Functional
map()
append
These provide flexible options to dynamically expand objects, similar to using classes.
Use cases like configuration, state management, and abstract data types often require the ability to merge properties.
So don‘t be afraid to "push" elements onto JavaScript objects when needed!