Javascript — Objects

Eishta Mittal
9 min readOct 31, 2022

In JavaScript, an object is an unordered collection (non-primitive data-type) of key-value pairs. Each key-value pair is called a property.

The key of a property can be a string. And the value of a property can be any value, e.g., a string, a number, an array, and even a function.

An object, is a reference data type. This means the variable assigned an object stores the reference to the object and not the value.

JavaScript Object Declaration

The syntax to declare an object is:

Object Literal

const object_name = {
key1: value1,
key2: value2
}

An object can be created with figure brackets {…} with an optional list of properties.

Optional here means that the object may have no property as well

let obj = {};    // valid// also valid object
const student = {
firstName: 'Ram',
class: 10
};

Object constructor

const student = new Object();
student.firstName = 'Ram';
student.class = 10;

Function Constructor

function Vehicle(name, maker) {
this.name = name;
this.maker = maker;
}
let car1 = new Vehicle('Fiesta', 'Ford');
console.log(car1.name); // Output: Fiesta

Object.create()

let obj = Object.create(prototype_object, propertiesObject)let footballers = {
position: "Striker"
}
let footBallerClone = Object.create(footBallers);
footBallerClone.position ; // Striker

Accessing Object Properties

  1. Dot Notation
objectName.key

2. Bracket Notation

objectName["propertyName"]

The bracket keyword works with any string combination, including, but not limited to multi-word strings.

somePerson.first name // invalid
somePerson["first name"] // valid

Accessing any property as determined at runtime

const propertyName = 'make';
myCar[propertyName] = 'Ford';
console.log(myCar);
// {make: 'Ford'}

JavaScript Nested Objects

// nested object
const student = {
name: 'John',
age: 20,
marks: {
science: 70,
math: 75
}

}
console.log(student.marks.science); // 70

JavaScript Object Methods

let school = {
name: 'Vivekananda School',
location : 'Delhi',
established : '1971',
20 : 1000,
displayInfo : function(){
console.log(`The value of the key 20 is ${school['20']}`);
}

}
school.displayInfo();
// The value of the key 20 is 1000

Inherited Properties

Those properties which have been inherited from the object’s prototype, as opposed to being defined for the object itself, which is known as the Object’s Own property.

To verify if a property is an object’s own property, we can use the hasOwnProperty method

const object1 = new Object();object1.property1 = 42;console.log(object1.hasOwnProperty('property1')); // true

Iterating over all keys of an object

  1. for…in loop
for (let key in person1) {
// Output : name, age, nationality
// and gender
console.log(key);
}

2. Object.keys(myObj)

3. Object.getOwnPropertyNames(myObj).

Deleting Properties

const student = {
firstName: 'Ram',
class: 10
};
console.log(student.class); // 10
delete obj1.propfirst
console.log(student.class); // undefined

Checking if a property exists

propertyName in objectName

Example:-

const student = {
firstName: 'Ram',
class: 10
};
console.log('firstName' in student); // true

The “non-existing property” problem- Optional Chaining

If we try to access any nested object properties, even if an intermediate property doesn’t exist.

let user = {}; // a user without "address" property  alert(user.address.street); // Error!

How to solve this?

  1. Using ternary operator
alert(user.address ? user.address.street : undefined);

2. Using && operator

alert( user.address && user.address.street && user.address.street.name ); // undefined (no error)

3. Using Optional Chaining

The optional chaining ?. stops the evaluation if the value before ?. is undefined or null and returns undefined.

alert( user?.address?.street ); // undefined (no error)

The variable before ?. must be declared

// ReferenceError: user is not defined
user?.address;

The variable must be declared (e.g. let/const/var user or as a function parameter). The optional chaining works only for declared variables.

Optional chaining on function properties

let userAdmin = {
admin() {
alert("I am admin");
}
};
let userGuest = {};
userAdmin.admin?.(); // I am admin
userGuest.admin?.(); // nothing happens (no such method)

We can use ?. for safe reading and deleting, but not writing

delete user?.name;   // valid
user1?.[key] // valid
userAdmin.admin?.() //valid

Property Flags

Object properties, besides a value, have three special attributes:-

  • writable – if true, the value can be changed, otherwise it’s read-only.
  • enumerable – if true, then listed in loops, otherwise not listed.
  • configurable – if true, the property can be deleted and these attributes can be modified, otherwise not.

Change the flags :-

  1. Single Property
Object.defineProperty(obj, propertyName, descriptor)

2. All the properties

Object.defineProperties(user, {
name: { value: "John", writable: false },
surname: { value: "Smith", writable: false },
// ...
});

Get the property description:-

  1. Single Property
let descriptor = Object.getOwnPropertyDescriptor(obj, propertyName);{  "value": "John",
"writable": true,
"enumerable": true,
"configurable": true
}

2. All the properties

Object.getOwnPropertyDescriptors(obj)

Sealing an object globally

  1. Object.preventExtensions(obj)- no addition of new properties
  2. Object.seal(obj)- configurable: false for all properties (no addition/removal)
  3. Object.freeze(obj): Forbids adding/removing/changing of properties. configurable: false, writable: false
  4. Object.isExtensible(obj): returns false if can’t add new properties.
  5. Object.isSealed(obj): returns true if object is sealed
  6. Object.isFrozen(obj): returns true if object is freezed

Ordered like an array

In an object, integer properties are sorted, others appear in creation order.

let codes = {
"49": "Germany",
"41": "Switzerland",
"44": "Great Britain",
// ..,
"1": "USA"
};
for (let code in codes) {
alert(code); // 1, 41, 44, 49
}

What is considerd as an integer?

The “integer property” term here means a string that can be converted to-and-from an integer without a change.

String(Math.trunc(Number("49"))); // "49", same, integer property
String(Math.trunc(Number("+49"))); // "49", not same "+49"
String(Math.trunc(Number("1.2"))); // "1", not same "1.2"

Property Value Shorthand

function makeUser(name, age) {
return {
name: name,
// ...other properties
};
}
// can be written asfunction makeUser(name, age) {
return {
name, // same as name: name
// ...
};
}

Property Names Limitations

Unlike normal variables, an object property can have a name equal to language-reserved words like “for”, “let”, “return”.

let obj = {
for: 1,
let: 2,
return: 3
};

If the property is different from string or symbol, it is automatically converted to string

let obj = {
0: "test" // same as "0": "test"
};
// both alerts access the same property (the number 0 is converted to string "0")
alert( obj["0"] ); // test
alert( obj[0] ); // test (same property)

Inheritance

All objects in JavaScript inherit from at least one other object. The object being inherited from is known as the prototype, and the inherited properties can be found in the prototype object of the constructor.

Defining Methods

objectName.methodName = functionName;   // 1

const myObj = {
myMethod: function(params) { // 2
// ...do something
},

// this works too!
myOtherMethod(params) { // 3
// ...do something else
}
};

Using ‘this’ for object reference

You can use ‘this’ within a method to refer to the current object.

function sayHi() {
console.log(`Hello, my name is ${this.name}`)
}
const manager = {
name: "John",
age: 27,
job: "Software Engineer"
}
manager.sayHi = sayHi;
manager.sayHi(); // Hello, my name is John'

Using the object instead of ‘this’

let user = {
name: "John",
sayHi() {
alert(user.name); // "user" instead of "this"
}
};

But this way is unreliable.

let user = {
name: "John",
sayHi() {
alert( user.name ); // leads to an error
}
};
let admin = user;
user = null;

// overwrite to make things obvious
admin.sayHi(); // TypeError: Cannot read property 'name' of null

‘this’ with Arrow functions

let user = {
firstName: "Ilya",
sayHi() {
let arrow = () => alert(this.firstName);
arrow();
}
};
user.sayHi(); // Ilya

Defining getters and setters

A getter is a method that gets the value of a specific property. A setter is a method that sets the value of a specific property.

Getters and setters can be either

  • defined using object initializers, or
  • added later to any object at any time using a getter or setter adding method.
  1. Using Object Initializer
const myObj = {
a: 7,
get b() {
return this.a + 1;
},
set c(x) {
this.a = x / 2;
}
};

console.log(myObj.b); // 8
myObj.c = 50;
console.log(myObj.a); // 25

2. By adding to already created object

const myObj = { a: 7};Object.defineProperties(myObj, {
'b': { get: function() { return this.a + 1; } },
'c': { set: function(x) { this.a = x / 2; } }
});
console.log(myObj.b); // 8
myObj.c = 50;
console.log(myObj.a); // 25

Comparing the Objects

  1. Comparing two different objects
const fruit = {name: 'apple'};
const fruitbear = {name: 'apple'};
fruit == fruitbear; // return false
fruit === fruitbear; // return false

2. Comparing two variables holding a single object

const fruit = {name: 'apple'};
const fruitbear = fruit;
fruit == fruitbear; // return true
fruit === fruitbear; // return true

Cloning Objects

1. Object.assign()

let user = {
name: "John"
};
let clone = {};for (let key in user) {
clone[key] = user[key];
}
clone.name = "Pete"; // changed the data in it
alert( user.name ); // "John"

The same can be done with Object.assign()

Object.assign(dest, [src1, src2, src3...])

Example:-

let user = { name: "John" };
let permissions1 = { canView: true };
let permissions2 = { canEdit: true };
Object.assign(user, permissions1, permissions2);// now user = { name: "John", canView: true, canEdit: true }

2. Spread operator

let clone = {...user};

3. JSON

let clone = JSON.parse(JSON.stringify(user));

4. Object methods

let clone = Object.fromEntries(Object.entries(user))

Nested Cloning

In nested objects, properties can be references to other objects.

let user = {
name: "John",
sizes: {
height: 182,
width: 50
}
};
let clone = Object.assign({}, user);
alert( user.sizes === clone.sizes ); // true, same object

Changing the value of nested object property reflects in both user and clone object — Spread and Object.assign does Shadow Cloning.

clone.sizes.width = 100;
console.log(user.sizes.width); // 100
console.log(user.sizes.width); // 100

This can resolved using JSON to clone the object. — Deep cloning.

JSON.stringify/parse only work with Number and String and Object literal without function or Symbol properties.

let clone = JSON.parse(JSON.stringify(({}, user)));clone.sizes.width = 100;
console.log(user.sizes.width); // 50
console.log(user.sizes.width); // 100

But if the object includes function or symbols, they are replaced with null/undefined.

const arrOfFunction = [
() => 2,
{
test: () => 3,
},
Symbol('4'),
];
console.log(JSON.parse(JSON.stringify(arrOfFunction)));
//[ null, {}, null ]

Object declared using const

The const declaration creates a read-only reference to a value. It does not mean the value it holds is immutable — just that the variable identifier cannot be reassigned.

const user = {
name: "John"
};
user.name = "Pete"; // (*)
alert(user.name); // Pete
// but the below is not possible
user = {name: "John 2"}
// TypeError: Assignment to constant variable.

Garbage Collection

Reachability

  • “reachable” values are those that are accessible or usable somehow. They are guaranteed to be stored in memory.
let user = {   name: "John" };// user is reachable user = null;// the object reference stored in user is cleared// user is not reachable

If you have two references:-

let user = {   name: "John" };
let admin = user;
user = null;
// the object is still rechable by admin so it is not garbage collected.

Interlinked Objects

function marry(man, woman) {
woman.husband = man;
man.wife = woman;
return {
father: man,
mother: woman
}
}
let family = marry({
name: "John"
}, {
name: "Ann"
});
image taken form javascript.info
delete family.father;
delete family.mother.husband;
// now the objects are not reachable

Outgoing references do not matter. Only incoming ones can make an object reachable.

Now the relation becomes:-

The basic garbage collection algorithm is called “mark-and-sweep”.

Steps performed are :-

  • The garbage collector takes roots and “marks” (remembers) them.
  • Then it visits and “marks” all references from them.
  • Then it visits marked objects and marks their references. All visited objects are remembered, so as not to visit the same object twice in the future.
  • …And so on until every reachable (from the roots) references are visited.
  • All objects except marked ones are removed.

--

--