If you come from Java or C#, think of a JavaScript object as an open-ended map: keys are strings or symbols, values are any type, and the shape can change at runtime. There is no compile-time field list for plain objects (TypeScript interfaces are erased at run time).
Object literals
The usual syntax builds an object with properties in one expression.
const user = { id: 1, name: 'Grace', active: true,};Shorthand when the property name matches a variable:
const name = 'Grace';const user = { name };Computed keys use an expression in brackets:
const field = 'id';const row = { [field]: 42 };Property access and existence
Dot notation works for valid identifiers; bracket notation accepts variables and special names.
user.name;user['name'];'toString' in user; // inherited methods countObject.hasOwn(user, 'name'); // own property onlyundefined means the key is missing or explicitly set to undefined; use hasOwn or in when the distinction matters.
References, mutation, and copying
Objects are assigned by reference. Two variables can point at the same object; mutating through one is visible through the other.
const a = { x: 1 };const b = a;b.x = 2;console.log(a.x); // 2Shallow copy: Object.assign({}, src), spread { ...src }, or structuredClone (modern engines) duplicate top-level properties; nested objects are still shared unless you deep-clone. See Pass by value and reference in JavaScript for primitives versus objects.
Symbols and non-string keys
Symbol values are unique opaque keys, useful for avoiding name clashes on shared objects.
const id = Symbol('id');const item = { [id]: 99 };Only Object.getOwnPropertySymbols enumerates symbol keys alongside Object.keys for strings.
When to use plain objects
Use literals for records, options bags, and JSON-shaped data. Reach for Map when keys are not strings/symbols or when you need stable iteration order without prototype noise. Classes and constructors (later in this series) fit when many instances share behavior on a prototype.