Suggest an editImprove this articleRefine the answer for “Adding a property to an object”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)**You add a property by assigning it with dot notation (`obj.key = value`) or brackets (`obj['key'] = value`), and also with `Object.assign()`, the spread syntax `{ ...obj, key: value }` and `Object.defineProperty()`.** Dot notation works when the property name is a valid identifier; brackets are needed for dynamic keys or names with special characters. `Object.assign()` adds several properties at once and mutates the original object, spread creates a new object and leaves the original untouched (the immutable way), and `Object.defineProperty()` lets you tune the descriptor precisely: `writable`, `enumerable`, `configurable`. ```javascript const user = { name: 'Maria' }; user.age = 25; // dot notation user['city'] = 'Kyiv'; // brackets Object.assign(user, { role: 'admin' }); const copy = { ...user, active: true }; // a new object ``` **Key point:** assignment, `Object.assign()` and `Object.defineProperty()` mutate the object, while spread returns a new copy.Shown above the full answer for quick recall.Answer (EN)Image**You add a new property with a plain assignment through dot notation or brackets, and when you need more control you use `Object.assign()`, the spread syntax or `Object.defineProperty()`.** Choosing the way comes down to two questions: is the key known in advance, and may the original object be mutated. ## Theory ### TL;DR - `obj.key = value` is the simplest way and works for valid identifiers. - `obj['key'] = value` is needed for dynamic keys and names with special characters. - `Object.assign(target, source)` adds several properties at once and mutates `target`. - `{ ...obj, key: value }` creates a new object and leaves the original unchanged. - `Object.defineProperty()` gives full control over the property descriptor. - `const` does not forbid adding properties: it pins the reference, not the object contents. ### Quick example ```javascript const user = { name: 'Maria' }; user.age = 25; // dot notation user['city'] = 'Kyiv'; // brackets console.log(user); // { name: 'Maria', age: 25, city: 'Kyiv' } ``` ### Dot notation and brackets With **dot notation** (`.`): ```javascript const user = { name: 'Maria' }; user.age = 25; // add a new property console.log(user); // { name: 'Maria', age: 25 } ``` It works when the property name is a **valid identifier**: no spaces, no special characters, not starting with a digit. With **brackets** (`[]`): ```javascript const user = { name: 'Maria' }; user['city'] = 'Kyiv'; console.log(user); // { name: 'Maria', city: 'Kyiv' } ``` This way fits when the key is held in a **variable**: ```javascript const key = 'email'; user[key] = 'maria@example.com'; ``` or contains **invalid characters**: ```javascript user['favorite color'] = 'blue'; ``` ### Object.assign() ```javascript const user = { name: 'Maria' }; Object.assign(user, { age: 25, city: 'Kyiv' }); console.log(user); // { name: 'Maria', age: 25, city: 'Kyiv' } ``` You can add several properties in one call. The method mutates the original object (the first argument) and returns it. The copy is shallow: nested objects are carried over by reference. ### Spread syntax ```javascript const user = { name: 'Maria' }; const updatedUser = { ...user, age: 25 }; console.log(updatedUser); // { name: 'Maria', age: 25 } ``` The original object is **not changed**, a **new copy** is created. This is the immutable way, and it is exactly what React, Redux and anything that compares state by reference need. If a key repeats, the later one wins, so `{ ...user, name: 'Oleh' }` overwrites the name. ### Object.defineProperty() ```javascript const user = {}; Object.defineProperty(user, 'age', { value: 25, writable: true, enumerable: true, configurable: true }); console.log(user); // { age: 25 } ``` It lets you configure the property precisely: make it read-only (`writable: false`) or hidden from iteration (`enumerable: false`), for example. Important: when the flags are not stated explicitly, all three default to `false`, unlike a normal assignment. ### Comparison of the ways | Way | Mutates the original object | Good for | | --- | --- | --- | | `obj.key = value` | Yes | simple cases | | `obj['key'] = value` | Yes | dynamic keys | | `Object.assign()` | Yes | adding many properties | | `{ ...obj, key: value }` | No | creating a new object | | `Object.defineProperty()` | Yes | special property settings | ### Common mistakes - **Thinking `const` forbids adding properties.** `const user = {}` pins the reference, not the contents: `user.age = 25` works. To forbid changes you need `Object.freeze()` or `Object.seal()`. - **Using dot notation with a variable.** `user.key = 1` creates a property literally named `"key"`, not the value of the variable `key`. For a variable you need brackets: `user[key] = 1`. - **Treating `Object.assign()` as a deep copy.** It is shallow: nested objects stay shared. For a deep copy use `structuredClone()`. - **Forgetting the flags in `Object.defineProperty()`.** Without explicit `writable`, `enumerable`, `configurable` the property is read-only and invisible to `Object.keys()` and `JSON.stringify()`. - **Mutating an object where a new reference is expected.** If state is compared by reference, a direct assignment triggers no update, and you need the spread instead.For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.