Think JavaScript objects are just fancy dictionaries?
They’re the backbone of how apps hold data and behavior, and getting them right makes your code simpler, faster to build, and easier to change.
In this post you’ll learn what properties and methods actually are, how to create and copy objects without surprises, and real code examples you can run today.
By the end you’ll know how to organize data like a pro and avoid the common traps beginners hit.
Core Concepts of JavaScript Objects Explained

Objects solve a simple but common problem. If you’re building a project that tracks 10 people and you need to store 3 characteristics for each one, you’d need 30 separate variables. That gets messy fast. Objects group related data together so you can manage everything in one place. Instead of person1Name, person1Age, and person1City scattered everywhere, you store all three inside a single person object.
An object is a collection of key/value pairs wrapped in curly braces. Each key (also called a property name) is paired with a value. Keys usually are strings, and if you pass something else like a number, JavaScript converts it to a string behind the scenes. Values can be any data type you want. Strings, numbers, booleans, arrays, other objects, or functions. When a value is a function, we call it a method instead of a property. Properties hold data. Methods hold behavior.
You access values by referencing their key name, and you can read or change any property whenever you need to. The object keeps everything organized, so instead of hunting through dozens of unrelated variables, you look inside one container. Objects are the foundation of how JavaScript structures complex information, and once you understand how keys and values work together, you’ll see them everywhere in your code.
JavaScript Object Creation Patterns and When to Use Each

JavaScript gives you a few different ways to create an object, and each one fits different situations. The most common pattern is the object literal, which is just a pair of curly braces with properties inside. You can also use a constructor function, which is a normal function that gets called with the new keyword. Constructor functions are capitalized by convention, like Person or Animal, to signal that they’re meant to create objects. There’s also Object.create(), which lets you build an object with a specific prototype, and new Object(), which is rarely used because the literal syntax does the same thing in fewer characters.
Most of the time, you’ll use the literal syntax. It’s readable, it’s fast, and it’s the default choice for simple objects that you only need one of. Constructor functions become useful when you need to create multiple similar objects from the same template, like 10 users who all share the same structure. Object.create() is less common, but it shines when you want fine control over inheritance. If you’re just starting out, stick with object literals until you have a specific reason to reach for something else.
Choosing the right pattern depends on whether you’re making a one-off object or building a reusable blueprint. Literals work for single objects. Constructor functions work when you need many instances. Object.create() works when prototype linking matters more than convenience.
Literal vs Constructor Function
Object literal syntax looks like this: const user = { name: 'Alice', age: 30 };. You write the data inline, and you get an object immediately. The constructor function pattern looks like this: function User(name, age) { this.name = name; this.age = age; }, then you call const user = new User('Alice', 30);. The new keyword creates an empty object, binds this to it, runs the function, and returns the object. Literals are shorter and easier to read when you only need one object. Constructors add overhead but let you stamp out multiple objects from the same blueprint.
Performance wise, literals are slightly faster because there’s no function call or new binding step. Readability wise, literals keep everything in one place, so you can see the structure at a glance. Constructors make sense when you’re building 10 or 100 similar objects and you want to centralize the logic. If you find yourself copying and pasting the same literal pattern, switch to a constructor. Otherwise, stick with {}.
Using Object.create for Prototype-Based Objects
Object.create(proto) takes one argument: the object you want to use as the prototype. It returns a new object that inherits properties and methods from that prototype. If you pass null, you get an object with no prototype at all, not even Object.prototype. This pattern is useful when you need precise control over the prototype chain, like when you’re building custom inheritance structures or avoiding unexpected inherited properties.
You’ll rarely need Object.create() as a beginner, but it’s the cleanest way to set up prototype-based inheritance without using constructor functions. For example, const cat = Object.create(animal); creates a cat object that can access properties defined on animal. The prototype link is direct and visible, which makes debugging easier when you’re tracing where a property comes from.
Working with and Modifying JavaScript Object Properties

Once you have an object, you access its properties using either dot notation or bracket notation. Dot notation looks like person.name and works when the property name is a valid identifier. Letters, numbers after the first character, dollar signs, or underscores. Bracket notation looks like person['name'] and works with any string, including multi-word keys like person['first name'] or property names stored in variables like person[keyVariable]. If the property name is dynamic or contains spaces, you must use brackets.
You can add new properties anytime by assigning a value to a key that doesn’t exist yet. person.city = 'Berlin'; adds a city property. Changing a property works the same way: person.age = 31; updates the age. Deleting a property uses the delete keyword: delete person.age; removes the age key entirely. Checking if a property exists can be done with 'age' in person, which returns true if the key is present (including inherited keys), or person.hasOwnProperty('age'), which only checks properties directly on the object. JavaScript objects are mutable, so you can reshape them as you build.
Common property operations:
Add: obj.newKey = value; creates a new property
Change: obj.existingKey = newValue; updates an existing property
Delete: delete obj.key; removes a property completely
Check existence: 'key' in obj or obj.hasOwnProperty('key')
Compute property name: obj[variableName] uses the variable’s value as the key
Rename: Copy the value to a new key, then delete the old one: obj.newName = obj.oldName; delete obj.oldName;
Copying JavaScript Objects: Reference Behavior, Spread, and Object.assign

When you assign an object to a new variable, JavaScript doesn’t make a copy of the data. It copies a reference to the same object in memory. If you write const a = { x: 1 }; const b = a;, both a and b point to the same object. Changing b.x = 2; also changes a.x because there’s only one object. If you want a separate copy, you need to explicitly create one using the spread operator or Object.assign().
The spread operator looks like const copy = { ...original }; (the three dots before original). Object.assign() looks like const copy = Object.assign({}, original);, where the first argument is the target and the second is the source. Both methods perform a shallow copy, which means they copy the top-level properties but leave nested objects and arrays as references. If your object has a property that’s an array, changing that array in the copy will also change the original.
Here’s a concrete example. You have const objectX = { name: 'John', age: 25, gadgets: { brand: ['apple', 'sony'] } };. You make a copy with const objectY = { ...objectX };. Then you change objectY.name = 'Mary'; and objectY.gadgets.brand[0] = 'hp';. Now objectX.name is still 'John' because the top-level name property was copied, but objectX.gadgets.brand[0] is now 'hp' because the gadgets object was shared between both copies. Shallow copying only goes one level deep.
| Split Method | Shallow or Deep | Notes |
|---|---|---|
| Assignment (=) | Reference only | Both variables point to the same object; no copy created |
| Spread ({ …obj }) | Shallow | Copies top-level properties; nested objects remain referenced |
| Object.assign() | Shallow | Merges sources into target; returns target; nested objects stay linked |
| JSON.parse(JSON.stringify(obj)) | Deep | Creates full copy but removes functions and non-JSON types |
Iterating Over JavaScript Object Properties

You iterate over an object’s properties using loops or built-in methods that return arrays. The for...in loop walks through all enumerable properties, including ones inherited from the prototype chain. Inside the loop, you must use bracket notation to read the value: for (let key in obj) { console.log(obj[key]); }. If you try obj.key, JavaScript looks for a property literally named "key" and usually returns undefined. Bracket notation treats the variable key as the actual property name.
Object.keys(obj), Object.values(obj), and Object.entries(obj) all return arrays. Object.keys() gives you an array of the object’s own property names. Object.values() gives you the values. Object.entries() gives you an array of [key, value] pairs, like [['name', 'Alice'], ['age', 30]]. These methods only include the object’s own properties, not inherited ones, so you don’t need to filter with hasOwnProperty(). They’re useful when you want to loop with forEach, map, or other array methods.
Common iteration patterns:
for…in loop: Iterates all enumerable properties (including inherited); use bracket notation inside the loop to access values
Object.keys(obj): Returns array of own property names; combine with forEach or map to process each key
Object.values(obj): Returns array of own property values; useful when you only care about the data, not the keys
Object.entries(obj): Returns array of [key, value] pairs; destructure inside forEach like Object.entries(obj).forEach(([k, v]) => ...)
Understanding Prototypes and Inheritance in JavaScript Objects

Every JavaScript object has an internal link to another object called its prototype. When you try to access a property, JavaScript first looks directly on the object. If it doesn’t find the property there, it checks the object’s prototype, then the prototype’s prototype, all the way up the chain until it reaches Object.prototype or null. This chain is how JavaScript implements inheritance. Methods like .toString() or .hasOwnProperty() live on Object.prototype, so every object can use them without defining them directly.
Constructor functions automatically set a prototype. When you write function Animal(type) { this.type = type; } and add Animal.prototype.speak = function() { console.log('sound'); };, every object created with new Animal() shares that speak method through the prototype. You don’t copy the method into each instance; they all reference the same function on the prototype. This saves memory and keeps behavior centralized. Object.create(proto) works the same way but gives you direct control: you pass the prototype object as the argument, and the new object inherits from it.
Prototypes explain why for...in sometimes loops over more properties than you expect. Inherited properties appear in the loop unless you filter them out with obj.hasOwnProperty(key). When you’re debugging or listing keys, knowing whether a property is “own” or inherited helps you understand where it’s coming from and whether it’s safe to modify.
How JavaScript Resolves Properties Through the Prototype Chain
When you read cat.name, JavaScript checks if cat has a property called name. If it does, JavaScript returns that value and stops. If cat doesn’t have name, JavaScript looks at the object referenced by cat‘s internal prototype link. If that object has name, JavaScript returns it. If not, it continues up the chain to that object’s prototype, and so on, until it either finds name or reaches null, which ends the chain. This process is automatic and invisible.
The lookup order is always bottom to top: the object itself, then its immediate prototype, then the prototype’s prototype, until you hit Object.prototype (which is the top of most chains) or null. If a property exists on both the object and the prototype, the object’s own property “shadows” the inherited one, so JavaScript never looks further up the chain. You can override inherited methods by defining a property with the same name directly on the object.
Methods, this Binding, and Behavior Inside JavaScript Objects

A method is just a function stored as a property on an object. When you call person.greet(), JavaScript runs the function and binds this to the object that invoked the method. In this case, person. That’s how methods can reference the object’s other properties using this.name or this.age. The value of this isn’t determined when you write the function; it’s determined when you call it. If you pull a method out of an object and call it standalone, this can change or become undefined.
Context changes depending on how you call the function. If you write const greet = person.greet; greet();, you’re calling the function without an object, so this might point to the global object (in non-strict mode) or be undefined (in strict mode). Arrow functions behave differently: they don’t bind their own this. They inherit this from the surrounding scope where they were defined. That’s useful inside callbacks or event handlers, but it means you can’t use an arrow function as a method if you need this to refer to the object.
You can manually set this using .call(), .apply(), or .bind(). person.greet.call(anotherObject) runs greet with this set to anotherObject. .bind() returns a new function with this permanently locked to whatever you pass. These tools let you control this when the default behavior isn’t what you need, like when passing a method as a callback and you want to preserve the original object reference.
Modern ES6+ Features for JavaScript Objects

Destructuring lets you pull values out of an object and assign them to variables in one line. Instead of const name = user.name; const age = user.age;, you write const { name, age } = user;. The variable names must match the property names unless you rename them with a colon, like { name: userName }. Nested destructuring goes deeper: const { address: { city } } = user; pulls city out of a nested address object. If a property is missing, you get undefined unless you provide a default, like { age = 18 } = user;.
The rest operator (...rest) inside destructuring collects all remaining properties into a new object. const { name, ...otherProps } = user; gives you name as a variable and otherProps as an object containing everything else. The spread operator (...obj) does the opposite: it expands an object’s properties into another object or merges multiple objects. const merged = { ...obj1, ...obj2 }; copies all properties from both objects, with obj2 overwriting any duplicate keys from obj1.
Optional chaining (?.) prevents errors when accessing deeply nested properties that might not exist. user.address?.city returns undefined if address is null or undefined, instead of throwing a TypeError. You can chain it as deep as you need: user?.profile?.settings?.theme. It’s especially helpful when working with API data or user input where you can’t guarantee every property will be present.
Modern ES6+ object features you’ll actually use:
Destructuring: Extract values from objects into variables; const { name, age } = person;
Nested destructuring: Access deeply nested properties; const { address: { city } } = person;
Rest properties: Collect remaining properties; const { name, ...rest } = person;
Spread operator: Copy or merge objects; const copy = { ...original }; const merged = { ...a, ...b };
Optional chaining: Safely access nested properties; user?.address?.city returns undefined if any part is missing
Getters, Setters, and Property Descriptors in JavaScript Objects

Getters and setters let you define functions that run when you read or write a property, but you call them like normal properties. No parentheses. Inside an object literal, you use the get and set keywords: get fullName() { return this.firstName + ' ' + this.lastName; }. When you access person.fullName, JavaScript runs the getter function and returns the result. If you try to call it with parentheses like person.fullName(), you’ll get a TypeError because fullName is a property, not a method.
Setters accept one parameter and typically update internal state. set fullName(value) { [this.firstName, this.lastName] = value.split(' '); } lets you assign person.fullName = 'Jane Doe';, and the setter splits the string and updates firstName and lastName. Getters and setters are useful when you want to compute values on the fly or validate input before storing it. They keep the interface clean. Consumers of your object don’t need to know there’s logic running behind the scenes.
Every property has a descriptor object that controls its behavior. The descriptor has three main flags: writable (can you change the value?), enumerable (does it show up in loops?), and configurable (can you delete or reconfigure the property?). You set these with Object.defineProperty(obj, 'key', { value: 42, writable: false, enumerable: true, configurable: false });. When you create a property the normal way, all three flags default to true, but defineProperty lets you lock down properties or hide them from iteration.
| Descriptor | Default Value | Meaning |
|---|---|---|
| writable | true | If false, the property value cannot be changed |
| enumerable | true | If false, the property won’t appear in for…in loops or Object.keys() |
| configurable | true | If false, the property can’t be deleted or have its descriptor changed |
Object Freezing, Sealing, and Immutability Techniques

Object.freeze(obj) makes an object completely immutable. You can’t add, remove, or change any properties. Trying to modify a frozen object in strict mode throws an error; in non-strict mode, the change silently fails. Freezing is useful when you want to guarantee that an object won’t be altered after creation, like configuration objects or constants. Keep in mind that freeze is shallow. Nested objects inside a frozen object remain mutable unless you freeze them separately.
Object.seal(obj) is less strict. It prevents adding or removing properties, but you can still modify the values of existing properties. Sealing is helpful when you want to lock the structure of an object but allow the data inside to change. Object.preventExtensions(obj) is even lighter: it only stops new properties from being added. You can still delete or modify existing ones. Each method gives you a different level of control over what can change.
If you need deep immutability (where nested objects are also protected), you can recursively freeze every nested object, or use a library like Immer that handles immutability for you. Another approach is to always create new copies instead of modifying objects in place. Spreading or using Object.assign() gives you a new object, so the original stays untouched. That pattern works well with React state or any scenario where you want to preserve a history of changes.
Immutability strategies:
Object.freeze(obj): Prevents all modifications. No adding, removing, or changing properties; strict immutability
Object.seal(obj): Prevents adding or removing properties; allows changing existing values
Object.preventExtensions(obj): Prevents adding new properties; allows modifying or deleting existing ones
Cloning-based immutability: Always create a new object with spread or Object.assign() instead of mutating the original; works well for shallow structures
Final Words
We jumped straight into objects as key‑value collections—properties can hold arrays, nested objects, or methods, and keys become strings. That’s the core idea.
Then we ran through creation patterns (literal, constructor, Object.create), property updates and dynamic names, copying vs reference behavior, iteration, prototypes and this binding, ES6 features, getters/setters, and immutability techniques. Each piece shows how objects act in real code.
Keep building tiny examples in your editor. Mastering javascript objects makes building real apps easier and more fun.
FAQ
Q: What is an object in JavaScript with example?
A: An object in JavaScript is a key-value collection that groups related data and behavior; for example: { name: ‘Ada’, age: 30, greet() { return ‘Hi’ } }.
Q: Can you make objects in JavaScript? How to write a JS object?
A: Yes — you create objects by writing literals like { key: value } or using constructors, e.g. new Object() or Object.create(proto). The literal {} is the simplest and most common.
Q: Why use objects in JavaScript?
A: Objects in JavaScript are used to group related data and functions, model entities, and attach behavior, which keeps your code organized, reusable, and easy to pass around.

