Sunday, 2 August 2020

Objects, Prototypes, and Classes in JavaScript

Object Literals :
==================
let person ={
  firstName:'Jim',
  lastName:'Cooper'
};

dynamic nature of javascript :
ex:

let person ={
  firstName:'Jim',
  lastName:'Cooper'
};
person.age=29;
person.isAdult=function(){ return this.age>=18;}

console.log(person.isAdult()); // true

ex 1:

let person ={
  firstName:'Jim',
  lastName:'Cooper',
  age:29,
  isAdult:function() {return this.age>=18;}
};


console.log(person.isAdult()); // true

ex 3:

function registerUser(fName,lName){
  let person ={
   firstName:fName,
   secondName:lName
  };
  console.log(person); // {firstName: "Jim", secondName: "Cooper"}
}

registerUser('Jim','Cooper');

ex : shorthand syntax


function registerUser(firstName,secondName){
  let person ={
   firstName,
   secondName
  };
  console.log(person); // {firstName: "Jim", secondName: "Cooper"}
}

registerUser('Jim','Cooper');

Object Literal Method Declaration :
=====================================

let person ={
   firstName:'Jim',
   secondName:'Cooper',
   age:18,
   isAdult() {return this.age>=18;}
};

person.isAdult(); // true

Inspecting Object Properties with Object.keys() and for...in :
==============================================================
Object.keys() accepts an object as argument and returns an array of all its(own) enumerable properties.

ex :

let person ={
   firstName:'Jim',
   secondName:'Cooper',
   age:18,
   isAdult() {return this.age>=18;}
};

Object.keys(person); // [firstName,secondName,age,isAdult]

for (let propertyName in person){
   console.log(propertyName);  // [firstName,secondName,age,isAdult]
}

so 'Object.keys' and 'for in' basically accomplish the same thing,
they give access to each of the property and method names on an object.

Object Equality and the Object.is() Function :
==============================================
Javascript Equality Operators

1.==
should be avoided. Useful only in rare cases.

Note : Not type-safe
ex :
"42" ==42 //true
0==false  // true
null==undefined // true
""==0      // true
[1,2] == "1,2"  // true


2.===
Most common.should be used in almost all cases.
Note : 
1.Type-Safe
2.Convenient/Concise
3.NaN not equal to NaN
 ex : NaN === NaN //false
4.+0 equals -0
  ex : -0 === +0 //true

3.Object.is()
Less common.Like === except for a few mathmatical differences.
   ex:  Object.is(person1,person2)
Note :
1.Type-Safe
2.Verbose   
3.NaN equals NaN
  ex : Object.is(NaN,NaN) // true
4.+0 does not equal -0  
  ex: Object.is(-0,+0) //false


Note : so for primitive types like strings, javascript compares their values,
but with objects,it compares their memory addresses.

Object Assign and Immutability :
=================================
Object.assign() method allows you to copy or merge the properties 
from one object to another object.

ex : 

let person1={
  firstName:'Jim',
  lastName:'Cooper',
  age: 29
};

let person2={};

Object.assign(person2,person1);

Object.is(person1,person2); //false
person1===person2           //false

Equality operator returns false because even though the properties
are the same between the two objects,they are not the same object in memory.

ex 2:
let person1={
  firstName:'Jim',
  lastName:'Cooper',
  age: 29
};

let healthStats = {
   height : 68,
   weight : 150
};

Object.assign(person1,healthStats);
console.log(person1); //{firstName: "Jim", lastName: "Cooper", age: 29, height: 68, weight: 150}

Note : Object.assign() merges the properties into that first object,
so it mutates it, and then it also returns the mutated object.

To avoid mutating the objects you're merging,it's helpful to pass in the empty object as the first parameter
to prevent any of the component objects from changing .

ex :

let person1={
  firstName:'Jim',
  lastName:'Cooper',
  age: 29
};

let healthStats = {
   height : 68,
   weight : 150
};

function mergeHealthStats(person,healthStats){
   return Object.assign({},person,healthStats);
}

let mergedPerson = mergeHealthStats(person1,healthStats);
console.log(mergedPerson); //{firstName: "Jim", lastName: "Cooper", age: 29, height: 68, weight: 150}

Object.assign method used with an empty object as the first parameter 
to avoid mutating the other objects.

Using Constructor Functions to Create Objects :
===============================================
'new' creates a new object,sets the context of the 'this' keyword to that new object.

ex: 
function Person(firstName,lastName){
 this.firstName = firstName;
 this.lastName = lastName;
}
let person=new Person('Jim','Cooper');
console.log(person); //{firstName: "Jim", lastName: "Cooper"}

using Object.create() :
=========================
let person2 = Object.create (
   Object.prototype,
   {
    firstName:{value:'Jim',enumerable:true,writable:true,configurable:true},
lastName:{value:'Cooper',enumerable:true,writable:true,configurable:true},
age:{value: 29,enumerable:true,writable:true,configurable:true}
   }
);

console.log(person2); // {firstName: "Jim", lastName: "Cooper", age: 29}

Object.create(proto,props) has two arguments.
The first argument i prototype for new object.
the second argument is the property descriptors.

Javascript Object Properties :
==============================

Using Bracket Notation to Access javascript properties.

ex : 

let person ={
  firstName:'Jim',
  lastName:'Cooper',
  age:29
};

console.log(person['lastName']); //Cooper

Note :
1.work with invalid Identifiers
2.Work with Variables

Square bracket notation allows access to properties
containing special characters and selection of properties using variables.

ex : const variable ='name';
     const obj = {name:'value'};
console.log(obj[variable]); // value

Modifying properties with property Descriptor :
===============================================
The Object.getOwnPropertyDescriptor() method returns a property descriptor
for an own property of a given object.

ex : 
let person ={
  firstName:'Jim',
  lastName:'Cooper',
  age:29
};

console.log(Object.getOwnPropertyDescriptor(person,"firstName"));
//{value: "Jim", writable: true, enumerable: true, configurable: true}

Object.defineProperty

syntax : Object.defineProperty(object,propertyName,descriptor);

By default a property defined with 'Object.defineProperty' isn't writable,
enumerable and configurable.


Note : if you make nested object read only,you're just preventing that pointer from being 
changed,so you can't point the name property to a new object.

ex : let person ={
       name:{
    firstName:'Jim',
secondName:'Cooper'
   },
  age:29 
 };
 
 Object.defineProperty(person,'name',{writable:false});
 person.name.firstName='Kris';
 console.log(person.name); // {firstName: "Kris", secondName: "Cooper"}
 
 'name' here is just a reference and javascript only prevents us from changing this 
 reference and will not stop us changing the properties of the object that this reference 
 points to.
 
 To make the object itself non-writable we can use Object's freeze method.
 
Note : Object.freeze works shallowly at the 'object' level,whereas
'Object.defineProperty' works at the property level.
Shallowly means,that if a property contains another object type,this object is  
affected by 'Object.freeze'. 

// Prevents any changes to an object
Object.freeze(object);

ex: 
  let person ={
       name:{
    firstName:'Jim',
secondName:'Cooper'
   },
  age:29 
 }; 
 
 Object.freeze(person.name);
 person.name.firstName='Kris';
 console.log(person.name); // {firstName: "Jim", secondName: "Cooper"}
 
 Using Enumerable Attribute :
 ============================
 By default, properties on an object are enumerable ,
 meaning we can enumerate over them with for in loops and list them with object.keys.
 
 ex:
 
 let person ={       
firstName:'Jim',
    secondName:'Cooper',    
age:29 
 }; 
 
 for(let propertyName in person){
   console.log(propertyName + ': '+ person[propertyName]);
 }

Output :
// firstName: Jim
// secondName: Cooper
// age: 29 

 ex :
  let person ={       
firstName:'Jim',
    secondName:'Cooper',    
age:29 
 }; 
 
 Object.defineProperty(person,'firstName',{enumerable:false});
 
 for(let propertyName in person){
   console.log(propertyName + ': '+ person[propertyName]);
 }
 
 output :
 // secondName: Cooper
 // age: 29
 
 Object.keys(person); // ["secondName", "age"]
 
 console.log(JSON.stringify(person)); // {"secondName":"Cooper","age":29}
 
 The 'enumerable' descriptor,when set to false on a property will hide it
 when looping over all properties using for...in loop.it also hides the property
 from the list of keys as obtained by Object.keys() and will not show up during 
 JSON serialization with JSON.stringify() as well.
 
Using Configurable Attribute :

Once 'configurable' property is set to false, it locks down that property and 
prevents from modifying 'enumerable' and even configurable.It also prevents from
deleting that property.

ex:  

let person ={       
firstName:'Jim',
    secondName:'Cooper',    
age:29 
 };

 Object.defineProperty(person,'firstName',{configurable:false });
 
 Object.defineProperty(person,'firstName',{enumerable:false }); // Cannot redefine property : firstName
 
 delete person.firstName; // cannot delete property 'firstName' 
 
 Object.defineProperty(person,'firstName',{configurable:true }); // Cannot redefine property : firstName

 So making a property non-configurable makes it so you can't change
 the configurable or enumerable attributes, and you can't delete the 
 property.
 
 Creating Property Getters and Setters :
 
 To create getters and setters,you have to use 'defineProperty'.
 
 ex: let person={
       name:{
     firstName:'Jim',
secondName:'Cooper'
   },
   age : 29
   };
   
   Object.defineProperty(person,'fullName',{get:function(){ return this.name.firstName + ' '+this.name.secondName;}});
   console.log(person.fullName); // Jim Cooper

ex 2 :
   
let person={
       name:{
     firstName:'Jim',
secondName:'Cooper'
   },
   age : 29
   };
   
   Object.defineProperty(person,'fullName',
   {get:function(){ 
      return this.name.firstName + ' '+this.name.secondName;
     },
    set:function(value){
  let nameParts=value.split(' ');
  this.name.firstName=nameParts[0];
  this.name.secondName=nameParts[1];
}
    });
person.fullName='Fred Jones';
   console.log(person.fullName); // Fred Jones

Javascript Prototypes and Interfaces :
======================================

Prototype:

A prototype is an object that exists on
every function in javascript.So every function in 
javascript has a prototype property.

ex : 
  let myFunction = function(){}
  console.log(myFunction.prototype); // {}
  
Objects do not have a prototype property.

ex : 
 let person={
   firstName:'Jim',
   lastName:'Cooper',
   age:29
 };  

console.log(person.prototype); // undefined 
   
ex : 
let person={
   firstName:'Jim',
   lastName:'Cooper',
   age:29
 }; 
 console.log(person.__proto__); // Object {}
 
 Object's prototype and a function prototype are used differently.
 
 Function's Prototype :
 A function's prototype is the object 'instance' that will become the prototype
 for all objects created using this function as a constructor.
 
 Object's Prototype :
 An Object's prototype is the object 'instance' from which the object is inherited.
 
 Note : A prototype,whether it's a functions prototype or an object's
 prototype, is actually an instance of an object in memory.
 
 
 ex :
 
 function Person(firstName,lastName){
  this.firstName=firstName;
  this.lastName=lastName;
 }
 
 console.log(Person.prototype); // Person {}
 let objJim=new Person('Jim','Cooper');
 console.log(objJim.__proto__); // Person {}
 console.log(Person.prototype === objJim.__proto__); // true
 
 ex :
 
 function Person(firstName,lastName){
   this.firstName=firstName;
   this.lastName=lastName;
 }
 
 Person.prototype.age=29;
 console.log(Person.prototype); // Person{ age: 19 }
 
 let jim = new Person('Jim','Cooper');
 let sofia = new Person('Sofia','Cooper');
 sofia.__proto__.age=19;

console.log(jim.__proto__); // Person { age: 19   } 
console.log(sofia.__proto__); // Person { age: 19 }

console.log(Person.prototype===jim.__proto__);

Note : The object instance that is the function's prototype 
becomes the prototype for all objects created from that prototype.

Instance vs Prototype Properties :

'hasOwnProperty' returns a boolean value indicating whether the object
on which you are calling it has a property with the name of the argument.

ex : 
function Person(firstName,lastName){
  this.firstName=firstName;
  this.lastName=lastName;
}

Person.prototype.age=29;
let jim=new Person('Jim','Cooper');
let sofia=new Person('Sofia','Cooper');

jim.age=18;

console.log(jim.hasOwnProperty('age')); // true
console.log(jim.age);  // 18

age value comes from Prototype

console.log(sofia.age); // 29



Note : hasOwnProperty does not look at the prototype chain of the object.

Changing a Function Prototype :

Change the function's prototype to point to a completely different object.

ex : function Person(firstName,secondName){
       this.firstName=firstName;
   this.secondName=secondName;
     } 
 
Person.prototype.age=29;
var jim=new Person('Jim','Cooper');
var sofia = new Person('Sofia','Cooper');

Person.prototype ={age:18};

console.log(Person.prototype); Object { age:18}
console.log(jim.age); // 29
console.log(sofia.age); // 29

let kris = new Person('Kris','Cooper');
console.log(kris.age);  // 18

Multiple level of Inheritance :

By default, all objects in javascript inherit from object, and object has no prototype.

ex : function Person(firstName,lastName){
       this.firstName=firstName;
   this.lastName=lastName;
     }
Person.prototype.age=24;
let jim=new Person('Jim','Cooper');
console.log(jim.__proto__);          // Person { age : 24 }
console.log(jim.__proto__.__proto__); // Object { }
console.log(jim.__proto__.__proto__.__proto__); // null
 
Creating Your Own Prototypal Inheritance Chains :

 ex :
   function Person(firstName,lastName,age){
     this.firstName=firstName;
this.lastName=lastName;
this.age=age;
Object.defineProperty(this,'fullName',{
   get:function(){
     return this.firstName+' '+this.lastName
   },
   enumerable:true
});
   } 

 function Student(firstName,lastName,age){
   Person.call(this,firstName,lastName,age);
   this._enrolledCourses=[];
   this.enroll=function(courseId){
     this._enrolledCourses.push(courseId);
   };
   
   this.getCourses = function(){
      return this.fullName+"'s enrolled Courses are : "+
         this._enrolledCourses.join(', '); 
   };
 
 }

Student.prototype=Object.create(Person.prototype);
Student.prototype.constructor = Student; 

let jim=new Student('Jim','Cooper',29);

console.log(jim); // Student {firstName: "Jim", lastName: "Cooper", age: 29,_enrolledCourses: Array(0), enroll: ƒ, getCourses: ƒ}
console.log(jim.__proto__); // Student {}
console.log(jim.__proto__.__proto__); // Person {}

jim.enroll('CS205');
jim.enroll('MA101');
jim.enroll('PS101');
console.log(jim.getCourses()); // Jim Cooper's enrolled Courses are : CS205, MA101, PS101

Javascript Classes :
======================
classes were introduced in ECMAScript version 6.
classes just offer a little cleaner syntax to accomplish it.

class Person{
 constructor(firstName,lastName,age){
  this.firstName=firstName;
  this.lastName=lastName;
  this.age=age;
  }
  // getter and setters
 get fullName(){
   return this.firstName+' '+this.lastName;
 }
 set fullName(fullName){
   var nameParts=fullName.split(' ');
   this.firstName=nameParts[0];
   this.lastName=nameParts[1];
 } 
 // adding functions
 isAdult() {
   return this.age >=18;
 }
}

let jim = new Person('Jim','Cooper',29);
console.log(jim); // {firstName: "Jim", lastName: "Cooper", age: 29}

console.log(jim.fullName); // Jim Cooper

jim.fullName='Fred Jones';
console.log(jim.fullName); // Fred Jones

console.log(jim.isAdult()); // true

Note : classes have prototypes,just like functions.
getter and setters live on the prototype,whereas other
properties live on the object instances.

console.log(jim); // {firstName: "Jim", lastName: "Cooper", age: 29}

Object.defineProperty(Person.prototype,'fullName',{enumerable:true}); 

console.log(jim); //{age: 29,firstName: "Jim",fullName: "Jim Cooper",lastName: "Cooper"}

class Student extends Person {
 constructor(firstName,lastName,age){
   super(firstName,lastName,age);
   this._enrolledCourses=[];
 }
 // static functions
 static fromPerson(person){
   return new Student(person.firstName,person.lastName,person.age);
 }
 
 enroll(courseId){
   this._enrolledCourses.push(courseId);
 }
 
 getCourses(){
   return this.fullName + "'s enrolled courses are : "+
          this._enrolledCourses.join(', ');
 }

} 

let jim=new Student('Jim','Cooper',29);
jim.enroll('CS101');
console.log(jim.getCourses()); // jim Cooper's enrolled courses are : CS101


let jimStudent=Student.fromPerson(jim);

console.log(jimStudent); // Student { firstName:Jim lastName:Cooper age:29 _enrolledCourses: }

Monday, 27 July 2020

Arrow Functions in Javacscript

1.Arrow with No parameters

ex : const f1 = () => {} // A function taking no arguments
     const f2 = _ => {}  // A function with one argument that doesn't use it.
 
 
2.Single parameter

ex : const add1 = (x) => x+1;
 
Note : Parentheses around a single parameter are optional. 
 
3.Multiple parameter
 ex :    const sum =  (num1,num2) => num1 + num2;
     const sum = (num1,num2) => {return num1 + num2;}
Note : if you pass more than one parameter, you need to wrap them in parenthesis.
 
4.Block body

if you need more than just a single line of code in your arrow function's body,
then you can use the "block body syntax".

ex : let fun1=(x,y) => {
         let z=10;
return x+y+z;
     }
fun1(1,3); // 14

ex 1 :
const upperizedNames = ['Mallik', 'Talks', 'Java'].map( name => {
  name = name.toUpperCase();
   return `${name} has ${name.length} characters in their name`;
});
//["MALLIK has 6 characters in their name", "TALKS has 5 characters in their name", "JAVA has 4 characters in their name"]

5.Object literals

ex : const details = name => ({ firstName: name });

Note : if you want to return objects from an arrow function ,
you need to wrap them in parentheses.

Note : Two main benefits of arrow functions
1.Concise and shorter syntax
    ex : let myFunction = (a) => a * 5;
     myFunction(10); // 50
 
2.No separate 'this' binding.

The arrow function does not have its own 'this'.
 ex :
  function Person(){
  this.age = 0;
 
  setInterval(() => {
    this.age++; // |this| properly refers to the Person object
  }, 1000);
}
 
var p = new Person();



Unlike a regular function, an arrow function does not bind 'this'.
Instead,'this' is bound lexically.

With an arrow function,the 'this' binding keeps its 
original binding from the context.

Note : Arrow function should not be used in object methods.

No curly brackets and implicit return with single-line arrow function.

ex : let myfunc = a => a * a;

wrap body in parenthesis to return object literal

ex : let myfunc = (first,last) => ({ firstName:first,lastName:last});

Summary :
1.you don't need to use curly brackets if there's only one statement in the function.
In the cases where you omit the curly brackets,you can also omit the 'return' keyword,
as the return is implicit.

2.'this' is bound lexically,meaning that fat arrow functions don't have their own 'this' and that 
'this' refers to the parent scope.

3.They can't be used as constructors or generators.

4.There's no arguments variable available with arrow functions.

what is 'this'?

Note : However, in javascript, the callback function ( and every traditional function)
get its own value of 'this'.

ex : const details = {
  name: 'Arfat',
  friends: ['Bob', 'Alex'],
  getFriends: function () {
    this.friends.forEach(function (friend) {
      console.log(this.name + " is friends with " + friend);
    });
  }
}
details.getFriends();
// undefined is friends with Bob
// undefined is friends with Alex

The 'this' value of getFriends is SHADOWED by the callback's own value.

Solution for above problem

1.use the that=this pattern

ex : const details = {
  name: 'Arfat',
  friends: ['Bob', 'Alex'],
  getFriends: function () {
    const that = this;
    this.friends.forEach(function (friend) {
      console.log(that.name + " is friends with " + friend);
    });
  }
}
details.getFriends();
// Arfat is friends with Bob
// Arfat is friends with Alex

In this case , that variable is found in the parent scope which was correctly 
bound to the details object.

2.A lot of methods in javascript take an optional 'this' argument.

ex : const details = {
  name: 'Arfat',
  friends: ['Bob', 'Alex'],
  getFriends: function () {
    this.friends.forEach(function (friend) {
      console.log(this.name + " is friends with " + friend);
    }, this);
  }
}
details.getFriends();
//  Arfat is friends with Bob
//  Arfat is friends with Alex

3.Arrow functions.

Arrow functions bind 'this' lexically.In other words,
Arrow functions always have a value of 'this' keyword taken 
from the parent scope.

ex : const details = {
  name: 'Arfat',
  friends: ['Bob', 'Alex'],
  getFriends: function () {
    this.friends.forEach(friend => {
      console.log(this.name + " is friends with " + friend);
    });
  }
}
details.getFriends();
// Arfat is friends with Bob
// Arfat is friends with Alex

Note :
1.Arrow function can't use 'arguments' special variable.
2.Arrow function can't be used as a constructor.
3.Arrow function can't change the 'this' binding.
4.Arrow function can't be used as generator functions.

Sunday, 26 July 2020

Understand the 'this' keyword in JavaScript

Objects are the basic building blocks in javascript.There's one special object available in javascript,the
'this' object.you can see the value of 'this' at every line of javascript execution.The value of 'this' is decided based on how the code is being executed.

Execution Context :

The environment (or scope) in which the line is being executed is known as the 'execution context'.
The javascript runtime maintain a stack of these execution contexts,and the execution context present 
at the top of this stack is the one currently being executed.The object 'this' refers to changes every time the execution context is changed.


Note : The value of 'this' differs depending on how a function is invoked,so we can't know the value of 'this' just by looking at the function itself,but we need to know the context in which the function is invoked.

'this' keyword refers to the object it belongs to

1.In a method,'this' refers to the owner object.
  ex :
   let person={
    firstName:"John",
lastName:"Doe",
getFullName : function(){
  return this.firstName+" "+this.lastName;
}   
   };
   
   person.getFullName(); // John Doe      
 
2.Alone,'this' refers to the global object.

Note : 'this' refers to a Global Object.

By default,the execution context for an execution is global - which means if a code is being executed as part of a simple function call,then 'this' refers to a global object.

The 'window' object is the global object in the case of the browser.And in a NodeJS environment, a special object called 'global' will be the value of 'this'.

ex :
function foo() {
  console.log(this); // window
}
foo(); 
console.log(this); // window


  
3.In a function,'this' refers to the global object.

 ex : Immediately Invoked Function Expression (IIFE)

  (function(){
console.log("Anonymous function invocation");
console.log(this === window);
})();

4.In a function,in strict mode,'this' is undefined.
  
  If strict mode is enabled for any function, then the value of 'this'   will be marked as 'undefined' as in strict mode.The global object refers   to 'undefined' in place of the 'window' object.
  
 ex :
   function foo () {
'use strict';
console.log("Simple function call")
console.log(this); // undefined
}

In strict mode the value of 'this' in a global-execution context is 'undefined'.

5.In an event,'this' refers to the element that received the event.

 ex : <button onclick="foo(this)"></button>
    function foo(e){
        console.log(e); // <button onclick="foo(this)"></button>
    }

6.Methods like call(),and apply() can refer this to any object.

Note : 'this' with the call and apply methods.

A function in Javascript is also a special type of object.Every function has 
'call','bind' and 'apply' methods.These methods can be used to set a custom value
to 'this' in the execution context of the function.

ex : let obj = {name:"Harish"};
     let greeting = function(a,b,c){
          console.log("welcome "+this.name+" to "+a+" "+b+" in "+c);
    };
  greeting.call(obj,"Newton","KOLKATA","WB"); 
   // welcome Harish to Newtown KOLKATA in WB
   
The first parameter in call() method sets the "this" value, which is the object, on which the function is invoked upon. In this case, it's the "obj" object above.

The rest of the parameters are the arguments to the actual function.

ex :  let obj = {name:"Harish"};
     let greeting = function(a,b,c){
          console.log("welcome "+this.name+" to "+a+" "+b+" in "+c);
    };
let args=["Newton","KOLKATA","WB"];
  greeting.apply(obj,args); 
   // welcome Harish to Newtown KOLKATA in WB 

The only difference between the 'call' and 'apply' methods is the way an 
argument is passed.In the case of 'apply',the second argument is an array 
of arguments,whereas in the case of the 'call' method,the arguments are passed 
individually.

The 'bind' method returns a new method with 'this' referring to the first argument passed.

ex : let obj = {name:"Harish"};
     let greeting = function(a,b,c){
          console.log("welcome "+this.name+" to "+a+" "+b+" in "+c);
    };
    let bound = greeting.bind(obj); 
bound("Newtown","KOLKATA","WB");   // welcome Harish to Newtown KOLKATA in WB

Note : 'bind' allows you to set the 'this' value now while allowing you to execute the function 
in the future,because it returns a new function object.

Note :
a) 'this' refers to a new Instance.

When a function is invoked with the 'new' keyword,then the function is known as
a constructor function and returns a new instance.In such cases,the value of 'this'
refers to a newly created instance.

ex : 
function Person(fn, ln) {
this.first_name = fn;
this.last_name = ln;

this.displayName = function() {
console.log(`Name: ${this.first_name} ${this.last_name}`);
}
}

let person = new Person("John", "Reed");
person.displayName();  // Prints Name: John Reed
let person2 = new Person("Paul", "Adams");
person2.displayName();  // Prints Name: Paul Adams

b) 'this' refers to an Invoker Object (Parent object)

In javascript, the property of an object can be a method or a simple value.
When an object's method is invoked, then 'this' refers to the object which contains the
method being invoked.

ex :
 function foo () {
'use strict';
console.log("Simple function call")
console.log(this); 
}

let user = {
count: 10,
foo: foo,
foo1: function() {
console.log(this);
}
}

user.foo()               //  “this” refers to user object instead of global object.
let fun1 = user.foo1;
fun1()                  //  this method is invoked as a simple function.
user.foo1()            // As foo1 is invoked as a object’s method

The function definition of 'foo1' is the same,but when it's being called as a 
simple function call,then 'this' refers to a global object.And when the same 
definition is invoked as an object's method,then 'this' refers to the parent 
object.so the value of 'this' depends on how a method is being invoked.

Summary :
1.By default,'this' refers to a global object, which is global in the case of NodeJS and
a 'window' object in the case of a browser.
2.When a method is called as a property of an object, then 'this' refers to the parent object.
3.When a function is called with the 'new' operator , then 'this' refers to the newly created instance.
4.When a function is called using the 'call' and 'apply' methods,then 'this' refers to the value passed 
as the first argument of the call and apply method.

Saturday, 18 July 2020

Arrays and Array methods in Javascript

Javascript array can store multiple elements of different data types.

ex : let arrofMixed=[1,"two","three",4];

In javascript there are two main ways to declare a new object or a new array.

1.Array literal
2.Array Constructor

1.Array literal 

synatx : let arrayName=[elem1,elem2,...elemN];

let arrofObjects =[
{name: 'Aristotle',living:false},
{name:'AlKhwarimi',living:false}
];

2.Array Constructor

synatx :

let arrayName = new Array();
let arrayName=new Array(Number length);
let arrayName=new Array(elem1,elem2,...elemN);


let arrData=new Array(5);
let arrNames=new Array("Mary","Tom","jack","jill");

Array Methods :
===============

1.push() - Adds one or more elements at the end of an array and returns new length of the array.
ex :
 let stocks =[{type:"Apple",year:1975}];
 stocks.push({type:"Microsoft",year:1976});
 stocks.push({type:"Amazon",year:1995},{type:"salesforce",year:2003});
 
 console.log(stocks) // [{type:"Apple",year:1975},{type:"Microsoft",year:1976},{type:"Amazon",year:1995},{type:"salesforce",year:2003}]
 
 Note : push() appends elements to the end of the given array.
 
2.pop() - Removes the last element from an array and returns that element.

ex : 
  let stocks =[
  {type:"Apple",year:1975},
  {type:"Microsoft",year:1976},
  {type:"Amazon",year:1995}
  ];
  
  poppedStock=stocks.pop();
  console.log(stocks)        // [{type:"Apple",year:1975},{type:"Microsoft",year:1976}]
  console.log(poppedStock); // {type:"Amazon",year:1995}
 
3.shift() - Removes the first element and return that element.
 ex : 
  let stocks =[
  {type:"Apple",year:1975},
  {type:"Microsoft",year:1976},
  {type:"Amazon",year:1995}
  ];
  
  shiftedStock=stocks.shift();
  console.log(stocks)        // [{type:"Microsoft",year:1976},{type:"Amazon",year:1995}]
  console.log(shiftedStock); // {type:"Apple",year:1975}

4.unshift() - Adds the element to the beginning of an array and return new array length.  

   ex : 
  let stocks =[
  {type:"Apple",year:1975},
  {type:"Microsoft",year:1976},
  {type:"Amazon",year:1995}
  ];
  
  let newstock={type:"salesforce",year:2003};
  
  unshiftedStock=stocks.unshift(newstock);
  console.log(stocks)        // [{type:"salesforce",year:2003},{type:"Apple",year:1975},{type:"Microsoft",year:1976},{type:"Amazon",year:1995}]
  console.log(unshiftedStock); // 4
  
5.reverse() - Reverse the elements of an array.
 
   ex : 
  let stocks =[
  {type:"Apple",year:1975},
  {type:"Microsoft",year:1976},
  {type:"Amazon",year:1995}
  ];
  
  stocks.reverse();
  
  console.log(stocks) // [{type: "Amazon", year: 1995},{type:"Microsoft",year:1976},{type:"Apple",year:1975}]

6.splice - adds and remove elements from an array.

synatx :
    array.splice(start,end) // to remove elements
array.splice(start,0,element1,element2,....) // to add elements.

ex : delete element from an array
  let stocks =[
  {type:"Apple",year:1975},
  {type:"Microsoft",year:1976},
  {type:"Amazon",year:1995},
  {type:"Salesforce",year:2003}
  ];
deletedStocks = stocks.splice(0,3);
console.log(stocks) // [{type:"Salesforce",year:2003}];
console.log(deletedStocks) // [{type:"Apple",year:1975},{type:"Microsoft",year:1976},{type:"Amazon",year:1995}];
ex : add element to an array

  let stocks =[
  {type:"Apple",year:1975},
  {type:"Amazon",year:1995},
  {type:"Salesforce",year:2003}
  ];
  
  stocks.splice(1,0,{type:"Microsoft",year:1976});
  
  console.log(stocks) // [{type:"Apple",year:1975},{type:"Microsoft",year:1976},{type:"Amazon",year:1995},{type:"Salesforce",year:2003}];

Note : The splice() method changes the contents of an array by removing or replacing existing elements and/or adding new elements,
in the original array.  
  
7.indexOf() - Returns the index of the first occurrence of the specified element in the array,or -1 if it is not found.

ex : let fruits = ["Banana", "Orange", "Apple", "Mango"];
      let IndexedFruit = fruits.indexOf("Apple");
  console.log(IndexedFruit) // 2

Note : 

indexOf() expects a value as first parameter. This makes it a good choice to find the index in arrays of primitive types (like string,boolean,number).
  
findIndex() expects a callback as first parameter.Use this if you need the index in arrays with non-primitive types (e.g. objects) or your find condition
is more complex than just a value.  

ex : let stocks =[
  {type:"Apple",year:1975},
  {type:"Amazon",year:1995},
  {type:"Salesforce",year:2003}
  ];
  
 let indexedStock =stocks.findIndex( x => x.type ==="Salesforce");
 console.log(indexedStock) //2
 
 findIndex() return the index of the first element in the array that satisfies the condition.
 if the condition is not satisfied,-1 is returned.
 
8.lastIndexOf - Returns the index of the last occurrence of the specified element in the array,
or -1 if it is not found.

  ex :
  let fruits = ["Apple","Banana", "Orange", "Apple", "Mango"];
      let lastIndexedFruit = fruits.lastIndexOf("Apple");
  console.log(lastIndexedFruit) // 3
  
9.join() - Returns string of all the elements separated by the specified separator.
 
The join() method joins all elements of an array into a string.

ex :
let alphabets =["A","B","C","D"];
let result = alphabets.join();
console.log(result); // A,B,C,D

Note : It accepts a "separator" as argument, but the default is already a comma (,).

str =arr.join([separator=',']);

10. slice() - Returns a new array with specified start to end elements.

The slice() method copies a given part of an array and returns that copied part
as a new array.it doesn't change the original array.

synatx :
  array.slice(from,until);
  
ex :
let alphabets =["A","B","C","D"];
let result = alphabets.slice(1,3);
console.log(result); // ["B","C"]

11. contact() - Returns new array by combining values of an array that is 
specified as parameter with existing array values.

The contact() method is used to merge two or more arrays.
This method does not change the existing arrays, but instead returns a new array.
ex :

let array1=[1,2,3,4,5];
let array2=[6,7,8,9,10];

let array3=array1.contact(array2);
or 
let array3=[].contact(array1,array2);

console.log(array3); // [1,2,3,4,5,6,7,8,9,10]

12. map() - loop over the array and return new array based on the value return.

ex :
let stocks =[
  {type:"Apple",year:1975},
  {type:"Amazon",year:1995},
  {type:"Salesforce",year:2003}
  ];
  
  let names=stocks.map(x => { return x.type });
  console.log(names) // ["Apple","Amazon","Salesforce"]
  
Array.prototype.map(callback,thisValue);
  
The map function have three arguments for the callback(currentValue,index,array).

currentValue : required,the current element being processed in the array.
index        : Optional, the index of the current element being processed in the array.
array        : Optional, the array map was called upon.

And "thisValue" is to use this when executing callback.  

ex : let names=stocks.map((item,index) => {return item.type+index});
     console.log(names) // ["Apple0", "Amazon1", "Salesforce2"]
 
ex : 

const myArray = [1, 2, 3];
 
// using the third argument to map
myArray.map((value, index, array) => {
  return array[index] + 1;
});

// [2,3,4] 

A this value is a special object which is related with the execution context of the function.

ex : 

let myObject = { name: 'myObject' };

[1,2].map(function(item) { 
  console.log(item); // 1, 2
  console.log(this === myObject); // true
}, myObject)
 

13. every() - return true or false if every element in the array satisfy the condition.
 
Checks if all elements of the array satisfies the provided callback function.

ex : let array =[0,2,4,6];
    let result =array.every (item => (item % 2 ===0));
    console.log(result) // true

Note : if all elements return true, then every() will return true.

ex : For Empty Arrays 
let nums = [];
let result = (nums.length) ? nums.every(n => n==5) : false;
 
14. reduce() - This method reduces the array to a single value(left to right).
returns a callback for each element of an array.
reduce passes the result of this callback(the accumulator) from one array element to the other.

The accumulator can be pretty much anything (integer,string,object,etc) and must be instantiated 
or passed when calling .reduce().

The reduce function takes four arguments.

1.Accumulator
2.Current Value
3.Current Index
4.Source Array

The reduce function returned value is assigned to the accumulator,
whose value is remembered across each iteration throughout the array
and ultimately becomes the final single resulting value.

ex  : 
 let pilots = [
  { id: 10,name: "Poe Dameron",years: 14},
  { id: 20,name: "Temmin",years: 30},
  { id: 41,name: "Tallissan Lintra",years: 16},
  { id: 99,name: "Ello Asty",years: 22}  
 ];
  
 let totalYears = pilots.reduce((accumulator, pilot) =>
   accumulator + pilot.years ,0); 
   
  console.log(totalYears) // 82

  ex : 
  
  get most experienced pilot
  
  let mostExpPilot = pilots.reduce((oldest, pilot) => {
  return (oldest.years || 0) > pilot.years ? oldest : pilot;
}, {});

console.log(mostExpPilot) // {id: 20,name: "Temmin",years: 30}

15.filter() - returns new array with all the elements that satisfy the condition.

A common use case of .filter() is with an array of objects through their properties.
ex : 
    let heroes = [
{name: "Batman", franchise: "DC"},
{name: "Ironman", franchise: "Marvel"},
{name: "Thor", franchise: "Marvel"},
{name: "Superman", franchise: "DC"}
    ];
let marvelHeroes = heroes.filter(hero => hero.franchise==="Marvel");
    console.log(marvelHeroes) // [{name: "Ironman", franchise: "Marvel"},{name: "Thor", franchise: "Marvel"}]
16.some() - return true if at least one element in the array satisfy.

Checks if any one of the element in the array satisfies the provided callback function.
 
ex  : let numbers=[-10,0,10];
       let result=numbers.some(x=>x<0);
   console.log(result) // true

ex : let numbers=[1,2,3,4,5];
       let result=numbers.some(x=>x<0);
   console.log(result) // true   
   
17. sort() - sort the elements of an array.

The array got sorted in lexicographical order (i.e. alphabetically),
so each integer actually got coerced into a string type.

In order to sort a list in numerical order, the sort() method needs to be passed a
comparison function.

ex : Aescending numerical order

// Sort numerically because default is lexicographical sort:
[3, 8, -10, 23, 19, -4, -14, 27].sort((a,b)=>a-b)
// Output: Array(8)  [ -14, -10, -4, 3, 8, 19, 23, 27 ]

ex : Descending numerical sort
// Sort numerically descending, largest to smallest:
[3, 8, -10, 23, 19, -4, -14, 27].sort((a,b)=>b-a)
// Output: Array(8) [ 27, 23, 19, 8, 3, -4, -10, -14 ]

Protecting the original array with .sort() :

The original array  intact and prevent it from being changed.
one way of using slice().
ex : 
const transactions = [16.37, 2.33, 4.55, 13.44, 1.25]
const sorted = transactions.slice().sort((a,b)=>a-b) // Make a copy with .slice()
console.log(sorted) // Array(5) [ 1.25, 2.33, 4.55, 13.44, 16.37 ]

The .sort() method sorts the array in-place,so if necessary we
can make a copy while sorting by using syntax [...array.sort().

using spread operator(...) or Object.assign() 

const transactions = [16.37, 2.33, 4.55, 13.44, 1.25]
const sorted = [...transactions].sort((a,b)=>a-b) // Make a copy with the spread syntax
console.log(sorted) // Array(5) [ 1.25, 2.33, 4.55, 13.44, 16.37 ]

const alsoSorted = Object.assign([], transactions) // Make a copy with Object.assign()
alsoSorted.sort((a,b)=>a-b)
console.log(alsoSorted) // Array(5) [ 1.25, 2.33, 4.55, 13.44, 16.37 ]    
   
18. reduceRight() - this method reduces the array to a single value (right to left).

ex : let arr = ["A", "B", "C", "D", "E"];
     let result =arr.reduceRight((previous, current) => previous + current);
     console.log(result) // EDCBA

The reduceRight() method works in the same way as the reduce() method, but in the opposite direction.

Note : The reduce() method starts at the first element and travels towards the last,whereas the reduceRight() method
starts at the last element and travels toward the first .

Friday, 10 July 2020

spread syntax or Object.assign() in Javascript

Object.assign() will only do a shallow copy and will only enumerate own props.

shallow copy using "Spread Synatx" or "Object.assign"

Spread syntax to copy/merge an object.

var obj = { foo : "foo", bar:"bar"};
var clonedobj= {...obj};

The Object.assign() method can be used to copy the values of all enumerable own properties
from one or more source objects to a target object, and it will return the target object

var obj= {foo:"foo",bar:"bar"};
var clonedObj=Object.assign({},obj);

Both spread syntax or Object.assign() used to copy/merge the enumerable properties
of an object to another object.

Note the empty {} as the first argument, this will ensure you don't mutate the original object.

Note : Problem with these two approaches is that it will just do the shallow copy.

Deep Copy :
Deep copy using JSON.stringify and JSON.parse

ex :

// Deep Clone
  obj = { a: 0 , b: { c: 0}};
  let deepClone = JSON.parse(JSON.stringify(obj));
  obj.a = 5;
  obj.b.c = 5;
  console.log(JSON.stringify(obj)); // { a: 5, b: { c: 5}}
  console.log(JSON.stringify(deepClone)); // { a: 0, b: { c: 0}}
  
  deepClone.a = 6;
  deepClone.b.c = 6;
  console.log(JSON.stringify(obj)); // { a: 5, b: { c: 5}}
  console.log(JSON.stringify(deepClone)); // { a: 6, b: { c: 6}}

1.Javascript objects are mutable and store by reference.
2.Object.assign() and spread syntax can be used for copy but it will be shallow copy.
3.JSON.stringify and JSON.parse can be used for deep copy.

Monday, 6 July 2020

Security with Lightning Locker

A) Javascript Strict mode Enforcement

Don't need to specify "use strict" in your code.

Lightning locker implicitly enables javascript strict mode everywhere Javascript strict mode makes code more secure ,robust and supportable.

1.Variables(var,let and Constant) must be declare before use.
2.To share code you must export/import variables and functions from/to modules.
3.The libraries you use must also work in strict mode.

B) DOM Access Containment

Lightning web components can't use window or document global properties to query DOM.

C) Secure Wrappers

Lightning Locker restricts the use of global objects by wrapping it in a secure version
of the object.

LockerService wraps standard objects like window,document and element inside 
a secure version of these objects(SecureWindow,SecureDocument and SecureElement)
as a way to control access to APIs and regions of the DOM.

When components are loaded, they are provided with the secure wrappers(secureWindow and secureDocument) in instead of the standard objects(window and document).

When a component invokes a method on the document or window object, the secure wrapper can apply appropriate security restrictions.


Locker Service API Viewer

Secure Document
Secure Element
Secure Window

D) Restricted access to Salesforce Global variables

Blocks access to some global javascript objects that are available to other salesforce features such as $A,Aura ,sfdc and sforce

E)Lightning Locker Disabled for Unsupported Browsers

Lightning Locker is enabled in the supported browsers for Lightning Experience, 
except for IE11. Lightning Locker is disabled for IE11. We recommend using supported 
browsers other than IE11 for enhanced security.

F) Arrays Proxied When Passed to Child Components

Avoid passing large arrays of objects more than one level down. For example, pass an array from parent to child but no further.Divide your data into multiple smaller arrays in the parent component.

G) Content Security Policy (W3C Standard)

1.All javascript libraries must be uploaded to salesforce static resources.
2.All external fonts,images,frames and CSS must use an HTTPS URL.
3.Script tags can't be used to load javascript.
4.Event handlers in HTML such as onClick,onChange,etc. can't use inline javascript.
4.CSP policy violations are logged in the browser's developer console.

Sunday, 14 June 2020

ES6 Spread Notation for LWC


The spread operator is three dots (...) preceding an array.
The spread operator is a new way to add elements to an array without mutating.

Push Vs Spread 

push    - mutates the original data.
spread  - does not mutate the original data.


<template>
  <lightning-card title="HelloWorld" icon-name="custom:custom14">
      <div class="slds-m-around_medium">
          <template for:each={contacts} for:item="contact">
              <li key={contact.id}>
                  {contact.name}, {contact.title}
              </li>
          </template>
          <lightning-button variant="base" label="Base" title="Base action" onclick={handleClick} class="slds-m-left_x-small"></lightning-button>
      </div>
  </lightning-card>
</template>


import { LightningElement, track } from 'lwc';

export default class HelloWorld extends LightningElement {
    @track contacts = [
        {
            id: 1,
            name: 'Amy Taylor',
            title: 'VP of Engineering',
        },
        {
            id: 2,
            name: 'Michael Jones',
            title: 'VP of Sales',
        },
        {
            id: 3,
            name: 'Jennifer Wu',
            title: 'CEO',
        },
    ];
    lastId = 3;
    
    handleClick() {
        const id = this.lastId + 1;
        const contact = {
            id,
            name: 'New Name',
            title: 'New Title',
        };
        this.lastId = id;
        this.contacts = [...this.contacts, contact];
    }
}


Note : 
1.Recreates the array with the new value.
2.Refreshes reactive variables the same way a @track variable does when changing its value.


ex 2 :

<template>
    
    <lightning-datatable
                key-field="id"
                data={data}
                columns={columns}
                is-loading={tableLoadingState}>
            </lightning-datatable>

 <lightning-button variant="brand" label="Add row" title="Add row" onclick={addRow} class="slds-m-left_x-small"></lightning-button>
</template>

import { LightningElement, track } from 'lwc';
const columns = [
    { label: 'Label', fieldName: 'name', type:"text"},
    { label: 'Website', fieldName: 'website', type: 'text' },
    { label: 'Phone', fieldName: 'phone', type: 'text'  }
];
export default class App extends LightningElement {
    @track data = [{name:'Mylabel' , website:'google.com' ,phone :'74126478963',amount : 45  }];
    @track columns = columns;
    @track tableLoadingState = false;

    addRow(){
          const newdata = {            
            name: 'Mylabel2s',
            website:'2google.com',
phone :'74126478963',
amount : 45
        };  
      // spread Operator
        this.data = [...this.data ,newdata ];

    }
   
}