Javascript — Objects
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
- 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
- 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?
- 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
– iftrue
, the value can be changed, otherwise it’s read-only.enumerable
– iftrue
, then listed in loops, otherwise not listed.configurable
– iftrue
, the property can be deleted and these attributes can be modified, otherwise not.
Change the flags :-
- 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:-
- 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
- Object.preventExtensions(obj)- no addition of new properties
- Object.seal(obj)-
configurable: false
for all properties (no addition/removal) - Object.freeze(obj): Forbids adding/removing/changing of properties.
configurable: false, writable: false
- Object.isExtensible(obj): returns false if can’t add new properties.
- Object.isSealed(obj): returns true if object is sealed
- 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.
- 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
- 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"
});
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.