Wednesday, March 29, 2017

ES6: Proxies

Proxies

Proxies allow you to overwrite the default behavior of certain operations such as get, set, etc. called ‘traps’. We can intercept certain functions and alter their default behavior using proxies.

const person = {name:’AK’, age: 26};

First it takes a target, which is what object you would like to proxy
Second, a handler which contains all the operations you wish to rewrite

const personProxy = new Proxy(person, {
get(target, name){
console.log(“Someone is requesting”, target, name);
return target[name].toUpperCase();
});
set(target, name,value){
if(typeof value === ‘string’) {
target[name] = value.trim().toLowerCase();
}
}

personProxy.name = “Arjun”; //setter saves lowercase trimmed value
personProxy.name; //getter returns uppercase Arjun




ES6: Generators

Generators

It is a function that you can start and stop, play and pause

Put an asterisk after function 

Uses yield keyword

function* listPeople(){
yield ‘AK’;
yield ‘BK’;
yield ‘CK’;
}

const people = listPeople();
-Returns a generator

Call .next() on it
Returns an Object

people.next();
-Returns AK object
people.next();
-Returns BK object

The generator has a ‘done’ property
After all yields are done, it gets set to true.


ES6: Maps and WeakMaps

Maps

If sets are to an array, maps are to an object

It has a key and a value instead of just values

const dogs = new Map();

- Call .set() to add something

dogs.set('Woofer',3);
dogs.set('Bowwower',5);

- .has() to check key
dogs.has('Woofer');
-true

- .get() to get value
dogs.get('Woofer');
-3

- .delete() to remove item
dogs.delete('Woofer');

You can iterate over Maps in 2 ways:
1.Use a forEach loop
dogs.forEach((val,key)=>console.log(val,key));
-Gives us all dogs
2.Use a for of loop
for(const dog of dogs){
    console.log(key,val);
}
//Can use destructing here
for(const [key,val] of dogs){
    console.log(key,val);
}
-Gives us an array of dogs

dogs.clear();

Useful for Meta-data tracking

We can use the object itself to get values instead of key strings

const clickCounts = new Map;
const buttons = document.querySelectorAll(‘button’);

buttons.forEach(button => clickCounts.set(button,0
button.addEventListener(‘click’, function(){
const val = clickCounts.get(this);
console.log(val);
clickCounts.set(this,val+1);
console.log(clickCounts);
});

));

WeakMap

Setting to null gets garbage collected, just like WeakSet

ES6: Sets and WeakSets

Set

A set is a unique array whose properties you can set only once.

You can’t access the items individually,
and it is not index based.

It is a list of items which we can add to, remove from and loop over

const people = new Set();
people.add(‘AK’);
people.add(‘BK’);
people.add(‘CK’);

>people.size
gives the length of the set

>people.delete(‘BK’);
deletes the item from the set without needing an index

>people.clear();
empties the set

>people.values();
We get a setIterator, which we can .next() over

const it = people.values();
it.next()
it.next()
You can also set a for of loop
for (const person of people){
console.log(person);
}

Declaring a set

1.Normal array like declaration
const students = new Set([‘AK’,’BK’,’CK’]);
2.Convert to set from an existing array
const dogs = [‘Woofer’,’Bowwower’];
const dogSet = new Set(dogs);

.has property
students.has(‘AK’);
-true

Set properties:
add
delete
clear
size
values

Example

Imagine you offer brunch in your restaurant and there is a line for it.
You can handle the line using Set.

const brunch = new Set();
brunch.add(‘AK’);
brunch.add(‘KB’)’
const line = brunch.values();
console.log(line.next().value); //line.next gives us the generator and .value gives us the value

When you call next on a Set, the item removes itself from the Set

You can still add people to the Set after using next

Weak Set

WeakSets can only contain an object type.
You can’t loop over it
There is no .clear() method
WeakSets clean themselves up. When the reference to an item is deleted, it garbage collects automatically

const weakSauce = new WeakSet([dog1, dog2]);

dog1 = null; //dog1 disappears after a few seconds automatically


Thursday, March 9, 2017

ES6 Classes and Extending Classes

1.class declaration

class Dog { //No paratheses after Dog

constructor(name,breed){
this.name=name;
this.breed=breed;
//Notice how we did not have to create const name and const breed

} //no comma here
//I repeat.. no comma after the function, just add on a new function below
bark(){
console.log(“Bark!”);
}
cuddle(){
console.log(“Love you owner”);
}
static info(){   //static method.
/*
Static method calls are made directly on the class and are not callable on instances of the class. Static methods are often used to create utility functions.
*/
console.log(“Dogs are nice”);
}
//getter 
//A getter is not a method. It is a property
get description(){
return(`${this.name} is breed ${this.breed}`);
}
//This getter would be called as dogObjectVariable.description;
//setter
set nicknames(value){
this.nick = value.trim(); //removes whitespaces
/*Setter note
Accessing the property setter by its own name inside the setter creates an infinite recursive function call.
So 
set nickname(valule){
this.nickname=value.trim();
would call setter nickname which calls setter nickname recursively causing a stack overflow
}
*/
}
//This setter would be set as dogObjectVariable.nicknames = "Snicky";
//getter for nicknames
get nicknames(){
return this.nick.toUpperCase(); 
}

}


const scooby = new Dog(“Scooby”,”Beagle”);


2.class expression

const Dog = class {

}

3.Extending classes and super

  class Animal {
    constructor(name) {
      this.name = name;
      this.thirst = 100;
      this.belly = [];
    }
    drink() {
      this.thirst -= 10;
      return this.thirst;
    }
    eat(food) {
      this.belly.push(food);
      return this.belly;
    }
  }

  class Dog extends Animal {
    constructor(name, breed) {
      super(name); //super initializes the class that this class is based off of
      this.breed = breed;
    }
    bark() {
      console.log('Bark bark I\'m a dog');
    }
  }

  const rhino = new Animal('Rhiney');
  const snickers = new Dog('Snickers', 'King Charles');

4.Extending arrays
We can create our own classes that are based on arrays.

  class MovieCollection extends Array {
    constructor(name, ...items) {
      super(...items);
      this.name = name;
    }
    add(movie) {
      this.push(movie);
    }
    topRated(limit = 10) {
      return this.sort((a, b) => (a.stars > b.stars ? -1 : 1)).slice(0, limit);
    }
  }

  const movies = new MovieCollection('Wes\'s Fav Movies',
    { name: 'Bee Movie', stars: 10 },
    { name: 'Star Wars Trek', stars: 1 },
    { name: 'Virgin Suicides', stars: 7 },
    { name: 'King of the Road', stars: 8 }
  );

  movies.add({ name: 'Titanic', stars: 5 });


*Whenever you extend a class, you need to create the thing that you’re extending first before you create a class of what you want. Just  call:
super();

*for…of loop only loops over iterable items

Saturday, March 4, 2017

ES6 Symbols

Primitive types:
Number
string
object
boolean
null
undefined

7th primitive type is symbol

Symbols are unique
We can’t loop over them

  const classRoom = {
    
    'Ganesh':{grade:50, gender:'male'},
    'Bunty':{grade:60, gender:'male'},
    'Bunty':{grade:70, gender:'female'},

  };
  //Instead of doing the above, we could use symbols

  const symbolRoom = {
    
    [Symbol('Ganesh')]:{grade:50, gender:'male'},
    [Symbol('Bunty')]:{grade:60, gender:'male'},
    [Symbol('Bunty')]:{grade:70, gender:'female'},

  };

//symbols are not enumerable
 //but we can grab all the symbols contained by symbolRoom

  const syms=Object.getOwnPropertySymbols(symbolRoom);
  //syms.forEach(sym=>{console.log(sym.value)});
  //forEach doesn't work here
  //But we can map over the symbols
  //symbols here are keys for the array symbolRoom, so we can access them
  const symbolData=syms.map(sym=> symbolRoom[sym]);
  console.table(symbolData);
  //Remember that Symbol starts with a capital S!

ES6 Promises

To begin with, fetch is an inbuilt method built into the browser and it works like ajax.
fetch() returns a promise, not immediate data.
A promise promises data that is not going to come immediately, but sometime in the future.

The ‘then’ function is a callback. It runs only when data comes back
‘then’ only runs when data successfully comes back
‘catch’ runs when there is an error

const postPromise = fetch(http://stuff);
postPromise //It doesn’t yet know that we expect json data
.then(data=>data.json()) //Converts to json
.then(data=>console.log(data) //prints the actual data
.catch(err=>{console.error(err);})

Manually creating a promise

const prom=new Promise((resolve,reject)=>{
resolve(“i am cool”); //Immediately resolves the promise
});
prom
.then(data=>{console.log(data);})
.catch(err=>console.error(err));
//prints “I am cool”

const prom=new Promise((resolve,reject)=>{
reject(Error(“i ain\’t cool”)); //Immediately rejects the promise
//**Wrap the error message in an ‘Error’ object
//to ensure that the error thrown shows the line no.
}); **/
prom
.then(data=>{console.log(data);})
.catch(err=>console.error(err));

//.all waits for all promises passed to complete
//.all is not on the prototype, it is in the class
Promise
.all([promise1,promise2])
.then(data=>{console.log(data);});


*It is nice to use arrow functions in setTimeout, so that it inherits ‘this’ from the parent element.

Spread and Rest

If we have to combine arrays, spread could come in handy.

const pizzas=["A","B","C"];
const special=["F","G","H"];

If we had to put "D" and "E" between those, we'd have to perform several concat() functions.
With spread, this becomes easy

const combined=[...pizzas,"D","E",...special]; //voila!

Spread takes the elements of an array and separately places those elements where the array was, i.e, inside the container!

Rest is the opposite of spread. While spread unpacks items in a container, rest can pack items into a variable. This can come in handy when we are using the arguments of an array and we want to address only say, the first two arguments first and then deal with the remaining 13 later.

sumJustTwo(12,51,16,27,515);

function sumJustTwo(a,b,...otherNumbers){
console.log("The sum of first two arguments is ",a+b);
}

ES6 Array: From, of, find and findIndex

Array.from - turns array-ish things into arrays
Array.of - creates arrays from arguments
These 2 are not on the prototype, they are a part of the Array class itself.

Array.from might be useful if we need to use something like a map function on a nodelist
We know that document.querySelectorAll(); returns a nodelist and not an array.
Thus, it is array-ish.

Array.from takes in a second argument which gives us the ability to map over that array
const peopleArray = Array.from(people, person=>person.textContent);

Another case would be if we have a function like 
sumAll(1,2,3,4,5,5,63,2,4,39);
We can handle its arguments like 
function sumAll(){
const nums=Array.from(arguments);
}
and then perform a reduce on it like
const total= nums.reduce((prev,next)=>{return prev+next;},0)

posts.find(post=> post.code===code);//returns the entry itself
posts.findIndex(post=> post.code===code);//returns the array's index # of the first entry that satisfies the condition


const doSomeSatisfy = ages.some(age=>age>10);
const doAllSatisty = ages.every(age=>age>10);




Sunday, February 26, 2017

ES6 - For Of Loop

Types of loops:

Regular for loop
for(let i=0; i<array.length; i++){}

For each loop
array.forEach(cut=>{console.log(cut);});
//can’t use break or continue over forEach

For in loop
for(const item in array){console.log(item);}
//iterates over added prototype functions too

for of (Does not work on objects)
for (const item of array){console.log(item);}
//does not iterate over added prototypes
//break and continue works too
//works on nodelists

Friday, February 24, 2017

ES6: Destructing

Destructuring allows us to access properties of an object and save them in variables all at once

const person={first:”A”,second:”B”,third:”C”};
To save these values, we would have had to write something like
function saveValues(){
const first=person.first;
const second=person.second;
const third=person.third;
}

const {first,second,third} = person;

console.log(first,second,third); //gives us A B C

We can rename the containers of values retrieved from objects or arrays too
const {first:one,second:two,third:three}=person;

We can also assign default values

const {first:one=“A”,second:two=“A”,third:three=“C”}=person;

Saturday, February 11, 2017

ES6 Arrow Functions - 'this'


‘this’ - Determines when you must use an arrow function
A normal anonymous function identifies the calling element as ’this’
But an arrow function inherits ‘this’ from its parent.

element.addEventListener(‘click’, function(){
  console.log(this);//‘this’ is element here
  setTimeout(function(){
    console.log(this) //‘this’ is ‘window’ here because the current function we are in is not bound to
    //any scope 
    //(unlike the previous function which was bound to element’s click event).
    },200);
}); 

Solution, use arrow function inside setTimeout.

element.addEventListener(‘click’, function(){
  console.log(this);//‘this’ is element here
  setTimeout(()=>{
    console.log(this) //‘this’ is still ‘element’ here because the current function we are in inherits ‘this’ 
    //from its parent 
    //our arrow function is bound to ‘element’, making ‘this’ equal to element.
},200);
}); 

If we are calling an unbounded function statement inside a bounded scope of a parent function, it goes out into global scope. We can use arrow function to preserve the value of ‘this’ instead of jumping out in the unbounded global scope.

element.classList.contains(‘classname’); //contains can be used to check for a class

*Functions that are not bound to any element, like setTimeout, have 'this' set to window.
*Arrow functions inherit 'this' from their lexical parent 



Thursday, February 9, 2017

ES6 Arrow Functions - 1

MAP applies a function to every array element and returns a new array

const names=[‘john’,’katy perry’];
const fullNames=names.map(function(name){return `${name} Mayer`;});

//using an arrow function (concise)
const fullNames2=names.map((name)=>{return `${name} Mayer`;});

//If one parameter, can omit parantheses
const fullNames3=names.map(name=>{return `${name} Mayer`;})

//If no parameters, use empty brackets ()
const fullNames5=names.map(()=>`cool Mayer`);

//If returning, can omit braces (Implicit return)
const fullNames4=names.map(name=>`${name} Mayer`);

//If returning an object expression, wrap object in ()
const race=“100m Dash”;
const winners=names.map(winner=>({name:winner, race}));

//two parameters
const winners=names.map((winner,i)=>({name:winner,position:i+1,race}));

//Arrow functions are anonymous, cannot be stack traced

FILTER returns an array of terms that result in an expression to true

const ages=[11,54,45,34,65,72,23,17];
const young=ages.filter(function(age){return age<18});

//with arrow function

const young2=ages.filter(age=>age<18);

Tuesday, February 7, 2017

ES6 Variables - 4

var is hoisted.
console.log(pizza);
var pizza=“macaroni”;// produces undefined

const and let are not hoisted.
console.log(pizza);
const pizza=“macaroni”;// undeclared variable error

*var is hoisted, let and const are not.
*Everytime a variable is accessed inside an execution context, if it is not there, its outer lexical value is used.
__

Extra: The Scope Chain 

1.Accessing a global variable locally
var a=10;
function b(){
console.log(a);
}
b();
//prints 10.
//because a exists globally

2.Accessing a local variable globally
function c(){var d=10;}
undefined
d;
//undeclared error.
//this is because function c's variable environment does not exist at a global level

3.Accessing a variable of lexical parent
function p(){var g=30; function q(){console.log(g)}; q();}
p(); //prints 30
//because g exists in the outer environment of function q, which is function p

4.Accessing a variable of lexical grandparent
function p(){var g=30; function q(){function r(){console.log(g);} r();}q();}
p();//prints 30
//because g is 30 in r's grandparent p and r's parent q does not have a value for g

5.Accessing a variable of parent despite having a grandparent 
function p(){var g=30; function q(){g=50;function r(){console.log(g);} r();}q();}
p();//prints 50
//because here, r's parent q has a value for g

If you assign a value to a variable that has not been declared, it will automatically become a GLOBAL variable given that the function is run and the assignment has been executed..

function a(){b=20;}
a(); //Gives undeclared error if a() is not run.
console.log(b);//prints 20

The Order of Execution:
-Whenever a function is come across, dive into its execution stack
-Every execution context has its own variable environment
-Each child can access its lexical parents' or lexical grandparents' or lexical great-grandparents' and so on's variables if they are not overwritten.
All of the above runs in the javascript engine
-But asynchronous events are handled by a different component of the browser
-When an event is fired, it get's placed on the event queue, which is attended to by the JS engine after its execution stack and global stack are empty.
-By FIFO service, each event is taken and an execution context for that function is created and executed..

function a(){
//I tried using setTimeout here, but setTimeout is an event not a pure function.
  var ms=3000+new Date().getTime();
  while (new Date<ms){}
  console.log("I made you wait 3 seconds. Ha ha");
}
document.addEventListener('click',function(){console.log("I was clicked!");});
a();
//Output:
//I made you wait 3 seconds. Ha ha
//I was clicked!

*Dive into execution stacks first
*Each stack has its own variable environment (inherits from lexical parents, variables are hoisted within the variable environments)
*Asynchronous events are executed in FIFO after pure JS execution stack is clear

ES6 Variables - 3


An immediately-invoked function expression (or IIFE, pronounced "iffy") is a JavaScript programming language idiom which produces a lexical scope using JavaScript's function scoping.
Runs itself immediately.

var name=“Arjun”;
//but Window has a name attribute too.
So we use IIFE
function(){
var name=“Arjun”;
}();

Let and const don’t need IIFE
{
const name=“Arjun”; //name is block scoped
}

for loop leaks its index variable.
for(var i=0;i<10;i++)
{
console.log(i); //prints 0 through 9
}
console.log(i); //prints 10 in global scope

If we have a time sensitive function in a for loop, the results may get skewed, unless we used an IIFE.
for(var i=0;i<10;i++)
{
setTimeout(function(){console.log(i);},1000); 
}
//prints 10, 10 times because in 1 sec the counter will have already run.
//replacing var with let prints out 0-9 because it acts like an IIFE.

IIFE

IIFE executes the function like it was assigned to a variable and run. It creates a function expression for the function and executes it. 

function test(){var name="Al";};
test();
//is the same thing as
(function(){var name="Al";})();
//The only difference is that the second one is executed instantly
//and the first one is executed when its name is called

IIFE Syntax
(function(){})();//is IIFE
!function(){}(); //is IIFE and returns true
-function(){}(); //is IIFE and returns NaN
$function(){}(); //is IIFE but requires JQuery
(function(){}();) //is IIFE

*IIFE produces lexical scope
*let and const behave like IIFE
*for loop does not leak

Monday, February 6, 2017

ES6 Variables - 2


let and const cannot be declared more than once *in the same block.
So if you accidentally name two lets or const the same in the same block, it gives an error.
If you declare an existing let inside a different block, it is allowed but creates, not redefines, a new let.
const cannot be updated. 

If an object is const, the structure of the object cannot change but its properties can change.
  const person={name:”Arjun”, age:26};
  person.age=27; 
  console.log(person); //shows age 27.

If you want the properties of an object to be unchangeable:
  const arjun=Object.freeze(person);
  arjun.age=27;
  console.log(arjun);//shows age 26

let and const
*can’t be declared more than once
*const properties can change
*Object.freeze

ES6 Variables - 1


var can be updated.
They can be redefined.

var width=100;
var width=200;
doesn’t warn us. We are creating 2 variables with the same name, so the first one gets over-written.

var are ‘function scoped’.
They are only available inside the function they are created.
If they are not inside any function, they are globally scoped in window.

Because var is function scoped, it can leak out of a global if statement because there is no function. 
if(age>12){var dogYears=100;}
console.log(dogYears); //this works
let and const however, are ‘block scoped’.
They don’t leak out of {}
if(age>12){let dogYears=100;}
console.log(dogYears); //this doesn’t work
functions are also blocks. So they don’t leak out of a function.

var
*doesn’t warn us
*leaks in global


ES6

I'm starting Wes Bos' ES6 (Ecma Script) course today. It is also supposed to cover some JavaScript fundamentals.

I will be summarizing what I learned from it here.

Saturday, February 4, 2017

JS30 Challenge Day 15 - localStorage



Last but not the least, and the longest one of them all!
I have added some extra functionality to my solution in this one.

form elements have a reset method in them
this.reset();

JSON.stringify();
JSON.parse();

    localStorage.setItem('items',JSON.stringify(items));

For the clearAll function, 
I had to define items as let instead of const because I need to reset it to [] an empty array in the clearAll function.

UncheckAll was pretty straightforward.
Just remember to define the new buttons outside the form, because we have already used event delegation on the form.

An event delegation is like giving a task to a responsible parent.

If you give the task to its kids, they may not behave, so you delegate it.