




An object can be created 4 different ways:
// prototype set to Object.prototypeconst obj = {a: 1}
function ObjConstructor(a) {this.a = a}// prototype set to the function ObjConstructorconst obj = new ObjConstructor(1)
// prototype set to the 1st argumentconst obj = Object.create(Object.prototype, {a: {value: 1,enumerable: true,writable: true,configurable: true}})
// A class is a template for creating an objectclass Obj {constructor(a) {this.a = a}}// prototype set to the class Objconst obj = new Obj(1)
When a function is invoked with new in front of it (a constructor call):
[[Prototype]] linkedthis binding for that function call
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:
[[Prototype]] property.undefined is returned. This series of links between objects forms the prototype chain.You can link 1 object to another using Object.create(..)
// Prototype is set to Object.prototypeconst obj1 = {a: 1};const obj2 = Object.create(obj1);obj2.b = 2console.log(obj2.a)


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:
false, silently fail in non-strict mode, or throw TypeError in strict mode.A property on an object can be set in 3 different ways:
const obj = {a: 1}
const obj = {}Object.defineProperty(obj, 'a', {value: 1,enumerable: true, // will it be visible when iteratingwritable: true, // can the property be editedconfigurable: true // can the property be deleted})
The 3rd is through a setter (see below).
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).
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:
const obj = {};Object.defineProperty(obj, "a",{get: function() {return 1},enumerable: true});

To test if an object has a property, use:
Object.hasOwn(..) to exclude the [[Prototype]] chain,in to include it.const obj1 = {a: 1};const obj2 = Object.create(obj1);obj2.b = 2console.log(Object.hasOwn(obj2, "a"))console.log(Object.hasOwn(obj2, "b"))console.log('---------')console.log("a" in obj2)console.log("b" in obj2)
for..in iterates over the list of enumerable properties on an object (including its [[Prototype]] chain)for..of with Object.entries doesn't include the [[Prototype]] chainconst obj1 = {a: 1};const obj2 = Object.create(obj1);obj2.b = 2for (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.

An object can be cloned in 4 different ways:
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:
{ ...obj } and structuredClone(..) are the preferred ways to do a shallow and deep clone.

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.
const obj = { a: 1 }Object.freeze(obj)// This will fail as obj has been frozenobj.a = 2console.log(obj)
Have any feedback about this note or just want to comment on the state of the economy?


