Ways to create an object
In JavaScript an object can be created in at least ten ways, from the {} literal to Object.fromEntries(). The choice depends on whether you need a template with methods, an explicit prototype, or just a copy of existing data.
Theory
TL;DR
- The object literal
{}is the most readable and most common way. new Object()does the same thing but looks dated and is barely used now.- A constructor function with
newand aclasscreate objects "from a template";classis syntactic sugar over the function. - A factory function simply returns an object, with no
newand no prototypal inheritance. Object.create(proto)sets the prototype explicitly.JSON.parse(),Object.assign(), the spread form{ ...obj }andObject.fromEntries()build an object from existing data.
Quick example
const user = { name: 'Oleh', age: 25, isAdmin: false };
// properties can be added and removed dynamically
user.city = 'Kyiv';
delete user.isAdmin;
console.log(user); // { name: 'Oleh', age: 25, city: 'Kyiv' }The literal and new Object()
The object literal is the simplest and most widespread way:
const user = {
name: 'Oleh',
age: 25,
isAdmin: false
};The same result through the constructor:
const user = new Object();
user.name = 'Oleh';
user.age = 25;new Object() does exactly what {} does, but takes longer to write and looks old fashioned, so it is hardly used today.
Templates: constructor function and class
Before class existed, templated objects were created with a constructor function:
function User(name, age) {
this.name = name;
this.age = age;
}
const oleh = new User('Oleh', 25);
const alice = new User('Alice', 30);
console.log(oleh.name); // "Oleh"When a function is called with new, the engine does the following under the hood:
- creates an empty object
{}; - assigns it to
this; - returns that object (unless the function explicitly returns another one).
The ES6 class is the same mechanism with a nicer syntax:
class User {
constructor(name, age) {
this.name = name;
this.age = age;
}
sayHi() {
console.log(`Hi, I am ${this.name}`);
}
}
const oleh = new User('Oleh', 25);
oleh.sayHi(); // "Hi, I am Oleh"Class methods live on User.prototype, that is, one copy of the method for all instances.
Factory function and Object.create()
Instead of class or new you can simply return an object:
function createUser(name, age) {
return {
name,
age,
sayHi() {
console.log(`Hi, ${this.name}`);
}
};
}
const oleh = createUser('Oleh', 25);
oleh.sayHi(); // "Hi, Oleh"A very convenient approach, especially when you need neither prototype nor inheritance. The downside is that every instance gets its own copy of the method.
Object.create() builds a new object with the given prototype:
const person = {
greet() {
console.log(`Hi, I am ${this.name}`);
}
};
const user = Object.create(person);
user.name = 'Oleh';
user.greet(); // "Hi, I am Oleh"It is used when the prototype chain has to be set explicitly. A special case, Object.create(null), gives a "clean" object with no inherited methods at all, which is handy as a dictionary.
Building from existing data
From a JSON string, the typical case for a server response:
const json = '{"name": "Oleh", "age": 25}';
const user = JSON.parse(json);
console.log(user.name); // "Oleh"It works with plain data types only, methods do not survive a trip through JSON.
Object.assign() creates an object out of other ones by copying their properties:
const base = { a: 1 };
const extra = { b: 2 };
const result = Object.assign({}, base, extra);
console.log(result); // { a: 1, b: 2 }The spread syntax does the same thing more briefly and is the modern standard for copying and merging:
const user = { name: 'Oleh', age: 25 };
const copy = { ...user, city: 'Kyiv' };
console.log(copy); // { name: 'Oleh', age: 25, city: 'Kyiv' }Object.fromEntries() assembles an object from an array of [key, value] pairs:
const entries = [['name', 'Oleh'], ['age', 25]];
const user = Object.fromEntries(entries);
console.log(user); // { name: 'Oleh', age: 25 }What to choose
| Way | Example | Notes |
|---|---|---|
| Object literal | { name: 'Oleh' } | the most common way |
new Object() | new Object() | rarely used |
| Constructor function | function User() { this.name = ... } | the old alternative to class |
class | class User { ... } | the modern OOP approach |
| Factory function | function createUser() { return {...} } | no this, it just returns an object |
Object.create(proto) | Object.create(person) | creates an object with a given prototype |
JSON.parse() | JSON.parse('{"a":1}') | creates an object from a string |
Object.assign() | Object.assign({}, obj1, obj2) | copies and merges objects |
| Spread syntax | { ...obj1, ...obj2 } | short copying syntax |
Object.fromEntries() | Object.fromEntries([['a', 1]]) | from an array of pairs to an object |
In short: the most popular way is {}, for inheritance use Object.create(), for templates use class or a constructor function, and for copying use { ...obj } or Object.assign().
Common mistakes
- Calling a constructor function without
new. In sloppy modethispoints at the global object and pollutes it; in strict mode you get aTypeError. - Expecting a deep copy from spread or
Object.assign(). Both copy shallowly: nested objects stay shared. For a deep copy usestructuredClone(). - Using
JSON.parse(JSON.stringify(obj))as a universal clone. Functions andundefinedare lost, aDateturns into a string, and circular references throw. - Confusing
Object.create(proto)withnew. The first only sets the prototype, the second also runs the constructor. - Creating methods in a factory function for thousands of objects. Every instance gets its own copy of the function, while a
classkeeps one on the prototype. - Using a plain
{}as a dictionary for arbitrary keys. Keys such as__proto__ortoStringclash with the prototype;Object.create(null)or aMapis safer.
Short Answer
Interview readyA concise answer to help you respond confidently on this topic during an interview.