
Creating
An object can be created 4 different ways:
Console
// prototype set to Object.prototype const obj = { a: 1 }
Console
function ObjConstructor(a) { this.a = a } // prototype set to the function ObjConstructor const obj = new ObjConstructor(1)
Console
// prototype set to the 1st argument const obj = Object.create(Object.prototype, { a: { value: 1, enumerable: true, writable: true, configurable: true } })
Console
// A class is a template for creating an object class Obj { constructor(a) { this.a = a } } // prototype set to the class Obj const obj = new Obj(1)
When a function is invoked with new
in front of it (a constructor call):
- 1. a new object is created
- ▫ a user-defined object type or
- ▫ 1 of the built-in object types that has a constructor function
- 2. it is
[[Prototype]]
linked - 3. it is set as the
this
binding for that function call - 4. unless the function returns its own alternate object, the new-invoked function call will automatically return the newly constructed object.

Prototype Chain
When an object is created, a property, [[Prototype]]
, is set on the object.
It allows an object to access properties of other objects.
You can see this by creating an object in the browser's console:

When you attempt to access an object property's value on an object, for example obj.a
, the engine invokes an internal [[Get]]
operation.
The engine will:
- 1. Look for the property on the object.
- 2. If it can't find it, it will then look for that property in the object that is referenced in the
[[Prototype]]
property. - 3. If the linked object doesn't have the property, the engine will check that object's linked object. This continues until the property is found or there are no more links. If no match is found,
undefined
is returned. This series of links between objects forms the prototype chain.
Creating a Link
You can link 1 object to another using Object.create(..)
Console
// Prototype is set to Object.prototype const obj1 = { a: 1 }; const obj2 = Object.create(obj1); obj2.b = 2 console.log(obj2.a)


Setting a Property
When you attempt to set a value, myObject.myProperty = 1
, the engine invokes an internal [[Put]]
operation. If the property is present, the operation will check:
- 1. Is the property an accessor descriptor (see "Getters and Setters" section below), call the setter.
- 2. Is the property a data descriptor with writable of
false
, silently fail in non-strict mode, or throw TypeError in strict mode. - 3. Otherwise, set the value to the property.
A property on an object can be set in 3 different ways:
Console
const obj = { a: 1 }
Console
const obj = {} Object.defineProperty(obj, 'a', { value: 1, enumerable: true, // will it be visible when iterating writable: true, // can the property be edited configurable: true // can the property be deleted })
The 3rd is through a setter (see below).
Getters and Setters
Getters and setters are properties that call hidden functions to retrieve and set values. When you define a property to have either a getter or a setter, its definition becomes an accessor descriptor (as opposed to a data descriptor).
For accessor-descriptors, the value and writable characteristics of the descriptor are ignored. Instead, the engine considers the set and get characteristics of the property (as well as configurable and enumerable).
Console
const obj = { get a() { return this._a_; }, set a(val) { this._a_ = val * 2; } }; obj.a = 2; console.log(obj.a)
Above, the value is stored into a variable _a_
.
The underscores in the name is just a convention.
It's is a normal object property.
A getter can be also be defined using a descriptor:
Console
const obj = {}; Object.defineProperty(obj, "a", { get: function() { return 1 }, enumerable: true } );

Inspecting
To test if an object has a property, use:
- ▪
Object.hasOwn(..)
to exclude the[[Prototype]]
chain, - ▪
in
to include it.
Console
const obj1 = { a: 1 }; const obj2 = Object.create(obj1); obj2.b = 2 console.log(Object.hasOwn(obj2, "a")) console.log(Object.hasOwn(obj2, "b")) console.log('---------') console.log("a" in obj2) console.log("b" in obj2)
Iterating
- ▪
for..in
iterates over the list of enumerable properties on an object (including its[[Prototype]]
chain) - ▪
for..of
withObject.entries
doesn't include the[[Prototype]]
chain
Console
const obj1 = { a: 1 }; const obj2 = Object.create(obj1); obj2.b = 2 for (prop in obj2) { console.log(`${prop}: ${obj2[prop]}`) } console.log('---------') for (let [key, value] of Object.entries(obj2)) { console.log(`${key}: ${value}`); }
When iterating over an object, order of iteration isn't guaranteed. If insertion order is required, use a Map instead of an object.

Cloning
An object can be cloned in 4 different ways:
Console
const obj = { a: 1 } const copy1 = { ...obj } const copy2 = Object.assign({}, obj) const copy3 = JSON.parse(JSON.stringify(obj)) const copy4 = structuredClone(obj)
The 1st 2 create a shallow copy. The last 2 create a deep copy. The difference is only relevant if an object property has a value of another object:
- ▪ shallow: the reference is copied.
- ▪ deep: the object is duplicated and a reference to this new object will be used as the value.
{ ...obj }
and structuredClone(..)
are the preferred ways to do a shallow and deep clone.

Immutability
Object.freeze(..)
creates an immutable object.
An object that can't be changed.
It calls Object.seal(..)
on the passed in object and marks all data accessor properties as writable: false
. Their values can no longer be changed.
This approach is the highest level of immutability that you can attain for an object.
Console
const obj = { a: 1 } Object.freeze(obj) // This will fail as obj has been frozen obj.a = 2 console.log(obj)