JavaScript Tutorial
indexjavascript history javascript versions environment setup variables and data typesOperatorsstrings and numbers programming constructs arrays built in objects functions objects call apply bind closures error handling debugging CallbacksJS in Browser
BOM and DOMBrowser EventsWeb APIAjaxJQueryMost popular librariesAdvanced
prototypes Object Oriented ProgrammingModulesAsynchronous programmingBinary DataInternationalizationreactangularjsProjectsMiscellaneous
typescriptJS ecosystemChrome dev toolstesting frameworksInterview questions and AnswersObjects in javascript
Everything except primitives is an object in JavaScript. An Object has properties and methods.Creating objects
You can create objects in many ways in JavaScript as mentioned below.- literal object - syntactic sugar for new Object(). Each object gets separate copy of properties/methods. but inheritance is possible using Object.setPrototypeof(myobject,prototypeObject)
- Constructor functions (class keyword in ES6 is syntactis sugar for constructor functions) - you need to use new keyword to create an object. As a convention, first letter must be Capital. Important point to note here is that you can share properties or methods using FunctionName.prototype syntax.
- using Object.create() - we can share the props among objects using Object.create. In factory functions, each object gets separate copy of methods. We can share same copy of method/prop using object.create which is memory efficient. Object.create creates the prototype chain.
- Object.assign
- factory functions - function that create and return objects created using any of the methods above
//creating objects using literals
let car1 ={
make:"honda",
price:20000,
features:['abs','alarm']
}
console.log(car1.make);
//output - honda
console.log("car 1 " + JSON.stringify(car1));
//output - car 1 {"make":"honda","price":20000,"features":["abs","alarm"]}
//create objects using new operator (constructor)
let car2 = new Object();
car2.make = "honda";
car2.price = 20000;
car2.features = ['abs','alarm'];
console.log("car 2 " + JSON.stringify(car2));
//output - car 2 {"make":"honda","price":20000,"features":["abs","alarm"]}
let car3 = new Array();
car3.push(car1);
car3.push(car2);
console.log("car 3 " + JSON.stringify(car3));
/*output - car 3 [{"make":"honda","price":20000,"features":["abs","alarm"]},
{"make":"honda","price":20000,"features":["abs","alarm"]}]*/
/*similarly we can also create custom constructor and
then create an object using new operator */
let Car = function(make,price,features){
this.make=make;
this.price = price;
this.features = features;
}
/*
new operator steps
- Creates a blank, plain JavaScript object.
- Adds a property to the new object (__proto__) that links to the constructor function's prototype
object
- Binds the newly created object instance as the this context (i.e. all references to this in the
constructor function now refer to the object created in the first step).
- Returns this if the function doesn't return an object.
*/
let car4 = new Car("Honda", 20000, ['abs','alarm']);
console.log("car 4 " + JSON.stringify(car4));
//output - car 4 {"make":"Honda","price":20000,"features":["abs","alarm"]}
//create objects using Object.create(prototypeObject, propertiesObject) method
//Advantage of using this method is that we can specify if the property is writable
//and or enumerable
let propObject = {
make:{value:"honda",writable:true,enumerable:true},
price:{value:20000,writable:false,enumerable:true},
features:{value:['abs','alarm'],writable:false,enumerable:true}
}
let car5 = Object.create(null,propObject);
console.log("car 5 " + JSON.stringify(car5));
//output - car 5 {"make":"Honda","price":20000,"features":["abs","alarm"]}
//below object will not inherit from Object. so it will not have methods like toString()
const dummy1 = Object.create(null);
//Object.assign
let o5 = Object.assign({},o1) // {} is required, otherwise o1 and o5 will refer to same object
o5.p = 11
console.log('o5 -> ' , o5);
console.log('o1 -> ' , o1);
Object Properties
Creating properties
Object.defineProperty can be used to create property.
let user = {
name: "Sagar"
};
//create new property "id" with value 333 and overwriting is not allowed
Object.defineProperty(user, "id", {writable:false,
enumerable:true, configurable:true, value:333
});
Property aspects
3 important aspects of objects properties are- owned or inherited
- Enumaerable or Non-enumerable
- string or symbol
- Querying props - propertyIsEnumerable(), hasOwnProperty(), Object.hasOwn(), in
- Traversing props - Object.keys, Object.values, Object.entries, Object.getOwnPropertyNames, Object.getOwnPropertySymbols, Object.getOwnPropertyDescriptors , Reflect.ownKeys, for...in, Object.assign, ... spread syntax
let user = {
name: "John"
};
let descriptor = Object.getOwnPropertyDescriptor(user, 'name');
alert( JSON.stringify(descriptor, null, 2 ) );
You can access all methods and properties of any object using below code.
//show all properties and methods
for (let key in car5){
console.log (key, typeof car5[key],car5[key]);
}
// output
/*
make string honda
price number 20000
features object [ 'abs', 'alarm' ]
*/
Prototypes
- Object.getProtoypeOf()
- Object.setPrototypeOf
Copying and cloning objects
By using loop
let user = { name: "John", age: 30 };
let clone = {}; // the new empty object
// let's copy all user properties into it for (let key in user) { clone[key] = user[key]; }
// now clone is a fully independent object with the same content clone.name = "Pete"; // changed the data in it
alert( user.name ); // still John in th
Object.assign method
You can also use the Object.assign method to copy objects
let user = {
name: "John",
age: 30
};
let clone = Object.assign({}, user);
To make a “real copy” (a clone) we can use Object.assign for the so-called “shallow copy” (nested objects are copied by reference) or a “deep cloning” function, such as _.cloneDeep(obj) from lodash library. Const objects can be modified. Property flags and descriptors can be used to make object properties constant. Functions that are stored in object properties are called methods. When a function is executed with new, it does the following steps:- A new empty object is created and assigned to this.
- The function body executes. Usually it modifiesthis, adds new properties to it.
- The value of this is returned.
Comparing 2 objects
- by using lodash _equal() method
- by stringifying and then comparing
Destructuring objects
//object desctructuring
let dimensions = {
width: 1100,
height: 2200
};
//extract only width prop from dimensions object
let {width} = dimensions;
//array destructuring
let arr = ["sagar", "salunke"]
let [firstName, surname] = arr;
let [a, b, c] = "abc"; // ["a", "b", "c"]
let [p1, p2, ...p3] = ["X", "Y", "Z", "and more";
More on Objects
// Objects - create and destroy
// serialization
// equality
// Immutable Object
// Copying or cloning (shallow and deep)
// Comparing 2 objects
// Props - add delete props, attributes, enumerate props, isOwned meaning
// simple way to create a object using literal expression
let user = {
name:"sagar",
email: "[email protected]",
isMarried : true,
mobile : "0400000000",
"address" : {
city:"brisbane",
postcode : 4000
},
skills : ["js","ts", ".net", "java", "selenium", "python", "Azure","aws"],
//this is mandatory. otherwise you will get ref error
showInfo: function(){console.log(this.name,this.email)},
salary:(function(){
var __salary = 90000;
//IIFE can be used to create private variables in object literal
//return the members that you want to expose
return {
getTaxBracket: function()
{
return __salary>90000 ? 1:2;
}
};
}())
}
//destroy object - just set it to null - garbage collector will take care after that
//user = null
if(user===null)
console.log("User is pointing to null")
//access prop
console.log("User Mobile ", user.mobile)
//call object method
console.log("User Info ", user.showInfo())
//serialiazation & Deserialiazation
// convert object to string - serialaization
let serialiazedUser = JSON.stringify(user)
console.log("String representation -> ",serialiazedUser)
// convert string to object - deserialaization
console.log("Object representation -> ", JSON.parse(serialiazedUser))
// equality
let user1 = user
if (user1==user){
console.log("user1 and user are same objects")
}
let user3 = {}
if (user1!=user3){
console.log("user1 and user3 are different objects")
}
// Immutable objects
// Undefined, Null, Boolean, Number, BigInt, String, Symbol - these are immutable - passed by value
// Objects are mutable by default - we can add/remove props of object
// We can make certain props immutable by setting writable attribute to false
//To prevent any types of modifications in a object, freeze the object
var obj = { foo: 'bar' };
Object.freeze(obj);
delete obj.foo //silently ignored
console.log("Frozen object ", obj)
// clone
// shallow copy means nested objects will not be copied. Nested object will be shared
//shallow copying using spread operator and Object.assign
const clonedUser = { ...user };
console.log("Cloned user", clonedUser);
const clonedUser2 = Object.assign({}, user);
// deep copy using JSON - not recommended as you may lose data
/*
Object literal notation vs JSON
The object literal notation is not the same as the JavaScript Object Notation (JSON).
- In JSON, The property name must be double-quoted, and the definition cannot be a shorthand.
- In JSON the values can only be strings, numbers, arrays, true, false, null, or another (JSON) object.
- A function value can not be assigned to a value in JSON.
- Objects like Date will be a string after JSON.parse().
- JSON.parse() will reject computed property names and an error will be thrown.
*/
const clonedUser3 = JSON.parse(JSON.stringify(user));
//using lodash
var deepCopy = _.cloneDeep(obj);
// comparing 2 primitives or objects
// == (type conversion happens - typeof operator) vs === (no type conversion happens)
// _.isEqual method of lodash compares object values recursively
//private props
console.log("Salary Bracket ", user.salary.getTaxBracket())
console.log("x ", user.x) //undefined
console.log("Salary ", user.salary.__salary) //undefined
//Shallow vs deep copy of objects
let user1 = {
name: "sagar",
id: 12,
address: {
city: "brisbane",
postcode: 4000
}
};
let user2 = user1; //not a copy
//true
console.log("user1===user2? ", user1 === user2);
let user3 = { ...user1 };
//false
console.log("user1===user3? ", user1 === user3, user3);
//true - address is a reference to nested object,
//so it is copied in shallow manner
console.log(
"user1.address===user3.address? ",
user1.address === user3.address,
user3
);
//Shallow vs deep copy of Arrays
let a1 = [{ a: 1 }, { b: 2 }];
//not a copy
let a2 = a1;
//true
console.log("a1===a2? ", a1 === a2, a2);
let a3 = [...a1];
//false
console.log("a1===a3? ", a1 === a3, a3);
//true - Shallow copy
console.log("a1[0]===a3[0]? ", a1[0] === a3[0], a3);
// props
//add new prop to user
user.landline = "0203020"
//remove prop
delete user.landline
// enumerate all props
console.log("Enumerating object")
for (var i in user) {
console.log(i);
}
for (const [key, value] of Object.entries(user)) {
console.log(`${key}: ${value}`);
}
//convert object into map of key,value
new Map(Object.entries(user));
//Prop attributes - enumerable, configurable, writable
//Object.defineProperty(obj, prop, descriptor)
Object.defineProperty(user, 'citizenship', {
enumerable: false,
configurable: false,
writable: true,
value: 'AUS'
});
console.log("new prop citizenship -> ", user.citizenship)
// check if prop is available on object and prototype chain
console.log("is citizenship prop in user object -> ",'citizenship' in user)
console.log("is toString prop in user object -> ",'toString' in user) //true
console.log("is toString owned prop in user object -> ", user.hasOwnProperty("toString")) //false
//destroy props
Object.getOwnPropertyNames(user).forEach(function (prop) {
delete user[prop];
});
// for enumerable and non-enumerable properties
for (const prop of Object.getOwnPropertyNames(user)) {
delete user[prop];
}
Web development and Automation testing
solutions delivered!!