Intermediate JavaScript

Front-end Bootcamp

Created by Kostas Minaidis | Dec 2018

Agenta

  • Variables & Scope In-Depth: var, let & const
  • Hoisting
  • Advanced Array Manipulation Methods
  • Working with the DOM
  • Events and User Input
  • Async: Callbacks, Promises
  • JSON & Fetch()
  • Prototypes & Function Constructors
  • Understanding `this` & Explicit Binding: call, apply, bind

JS Variables

var, let and const

Confused?

CodePen to the rescue:
https://bit.ly/2ElTkcl

General Rule:

"Use let only for loop counters or only if you really need reassignment. Everywhere else, use const."

Definition:

( Ανέλκυση )

"Hoisting is a JavaScript mechanism where variables and function declarations are moved to the top of their scope before code execution."

"Of note however, is the fact that the hoisting mechanism only moves the declaration. The assignments are left in place."
doSomething();

for (let i = 0; i <= 10; i++) {
    console.log(i);
}

function doSomething() {
    console.log('function executed');
}

"Although the doSomething() function is called before it's defined,
the code still works."

"In fact, the for loop isn't started until the JavaScript engine figures out that doSomething() is defined and can execute the call."

Best Practices
  • Place your variable and function declarations at the top section of the scope in which they are declared
  • Place value assignments to variables before accessing them
CodePen Examples:

Variable Hoisting: https://bit.ly/2GaIZBu

Function Hoisting: https://bit.ly/2L5vMZt

Function Variables Hoisting: https://bit.ly/2zPhIzc

Undeclared Variables: https://bit.ly/2EmZQQ0

Manipulating Arrays

Recapping: push, pop, shift, unshift

let todo = [ ];
todo.push("ENTRY");
[ "ENTRY" ]
todo.push("AT THE END");
[ "ENTRY", "AT THE END" ]
todo.shift();
[ "AT THE END" ]
todo.unshift( "AT START" );
[ "AT START", "AT THE END" ]
todo.pop();
[ "AT START" ]

Tip: You can also create a new Array
using the following syntax:

let someArray = new Array( 1, "text", false );

...but we tend to prefer the [ ] syntax.

Manipulating Arrays

Array.reverse()

let listOfNumbers = [ 1, 2, 3, 4, 5 ];
listOfNumbers.reverse();
console.log( listOfNumbers );

Will output

[ 5, 4, 3, 2, 1]

WARNING: reverse() will mutate (change) the contents of the Array!

Quick Practice!
More on Array.prototype.reverse()

Array.sort()

Sorts an Array lexicographically using the ordered Unicode character table
or a custom sorting function. Works on a character-by-character basis.

let someNames = [ "Eve", "Adam", "Bob", "Bill", "adam" ];
console.log( someNames.sort() );
[ 'Adam', 'Bill', 'Bob', 'Eve', 'adam' ];

Warning: Numbers get converted to Strings and are compared alphabetically:

let someNumbers = [ 2, 11, 1 ];
console.log( someNumbers.sort() );
[ 1, 11, 2 ]

So how do we compare a list of numbers using sort()?

We need to pass a custom sorting function
as an argument to the sort() function.

let someNumbers = [ 2, 11, 1 ];
let sorting = function( a, b ){
if ( a < b ){ 
    return -1; 
} else if ( a > b ){ 
    return 1; 
} else { return 0; }	// a === b
}
console.log( someNumbers.sort( sorting ) );
[ 1, 2, 11 ]

Watch: Coding Train - Custom sorting function for Array sort()

Let's slice some Arrays!

Array.slice( start, until )

Returns a portion of the Array beginning at the start index
up to (but not including) the until index.

let animals = ['ant', 'bison', 'camel', 'duck', 'elephant'];
// Index -->     0       1        2        3         4
console.log( animals.slice( 2, 4 ) );
[ "camel", "duck" ]
console.log( animals.slice( 1, 5 ) );
[ "bison", "camel", "duck", "elephant" ]

If the second parameter (until) is omitted, slice extracts through the end of the Array.

console.log( animals.slice( 2 ) );
[ "camel", "duck", "elephant" ]

Tip: Using Array.slice() to clone an Array

When the first slice argument is omitted, it is automatically set to index 0.

let numbers = [ 1, 2, 3, 4 ];
let reversedNumbers = numbers.slice();
reversedNumbers.reverse();
console.log( numbers );
[ 1, 2, 3, 4 ]
console.log( reversedNumbers );
[ 4, 3, 2, 1 ]

Visualisation

Array.concat( Array, Array )

Merges 2 or more Arrays
let array1 = ['a', 'b', 'c'];
let array2 = ['d', 'e', 'f'];
let merged = array1.concat( array2 );
console.log( merged );
// merged === Array ["a", "b", "c", "d", "e", "f"]
Quick Practice!

Array.splice( start, deleteCount )

Removes or adds elements to an Array, and returns the removed/added elements.
let animals = ['ant', 'bison', 'camel', 'duck', 'elephant'];
// Index -->     0       1        2        3         4
Remove 1 element from index 3
let duck = animals.splice( 3, 1 );
console.log( duck );
"duck"
// animals:
             [ 'ant', 'bison', 'camel', 'elephant' ]
// Index -->     0       1        2          3
Remove 2 elements from index 1
let bisonAndCamel = animals.splice( 1, 2 );
// animals:
[ 'ant', 'elephant' ]

Array.splice( start, deleteCount )

let animals = ['ant', 'bison', 'camel', 'duck', 'elephant'];
// Index -->     0       1        2        3         4
Remove 1 element from index 2, and insert 'monkey'
animals.splice( 2, 1, 'monkey' );
            [ 'ant', 'bison', 'monkey', 'duck', 'elephant' ]
//--> Index     0       1        2         3          4
Insert 1 element at index 3
animals.splice( 3, 0, 'gorilla' );
[ 'ant', 'bison', 'monkey', 'gorilla', 'duck', 'elephant' ]
A Visual Guide to Array.prototype.splice

Array.map()

Creates a new array with the results of calling a provided function
on every element in the calling array.

let numbers = [ 2, 4, 8 ];
let modify = function( element ){
    return element * element;
} 
let squares = numbers.map( modify );
console.log( squares );
[ 4, 16, 64 ]

Array.map() Under the hood...

let numbers = [ 2, 4, 8 ];
function modify( element ){
    return element * element;
}
let results = numbers.map( modify );

[ 2, 4, 8 ]
modify( 2 ); --> 2 * 2 --> 4 --> [ 4 ]

[ 2, 4, 8 ]
modify( 4 ); --> 4 * 4 --> 16 --> [ 4, 16 ]

[ 2, 4, 8 ]
modify( 8 ); --> 8 * 8 --> 64 --> [ 4, 16, 64 ]

Array.forEach( test )

let numbers = [ 2, 4, 8 ];
function modify( element ){
    return element * element;
}
let results = numbers.forEach( modify ); // undefined
results = numbers.map( modify );         // [ 4, 16, 64 ]

Array.filter( test)

Runs the custom test function for all Array elements.
Creates a new Array with all the elements that passed the test function.

let ages = [ 15, 22, 23, 44, 11, 18 ];
function isAdult( age ){
  return ( age > 18 ); 
}
let passed = ages.filter( isAdult );
[22, 23, 44] // passed 

CodePen https://bit.ly/2P6pwlx

Array.filter() Under the hood...

let ages = [ 15, 22, 44, 11 ];
function isAdult( age ){ 
    return ( age > 18 ); 
}
let passed = ages.filter( isAdult );

[ 15, 22, 44, 11 ]
isAdult( 15 ); --> ( 15 > 18 ) --> false --> [ ]

[ 15, 22, 44, 11 ]
isAdult( 22 ); --> ( 22 > 18 ) --> true --> [ 22 ]

[ 15, 22, 44, 11 ]
isAdult( 44 ); --> ( 44 > 18 ) --> true --> [ 22, 44 ]

[ 15, 22, 44, 11 ]
isAdult( 11 ); --> ( 11 > 18 ) --> false --> [ 22, 44 ]

Array.reduce()

Goes through each element, runs a function and returns an accumulator.

let numbers = [ 12, 8, 10, 5, 5 ];
let sum = function( accumulator, currentValue ){

    accumulator = accumulator + currentValue;
    return accumulator;

}
numbers.reduce( sum, 0 );
 

Quick mention: Chaining

CodePen https://bit.ly/2RhnZL7

Let's practice on the
Array methods!

https://goo.gl/qtoLF4
https://goo.gl/Vh8bMf

DOM | The Document Object Model

The Document Object Model (DOM) is the programmable interface of web pages.

In other words, it allows JavaScript programs to read and manipulate the page content, structure and CSS styles.

DOM is basically a tree-like
structure of the web page

Live DOM Viewer

All objects of an HTML page are included and can be manipulated by the DOM.

The DOM is an interface that allows programs and scripts to dynamically update webpages: add and delete HTML elements, move elements to another place, change their CSS style, and more.

What does DOM contains?
  • Elements: <head>, <body>, <div>, etc.
  • Attributes: class, id, href, src, etc.
  • All text in-between (including spaces)

All these objects are known as DOM Nodes.

Example of Nodes?
 Google 
  • <a> is an Element Node
  • href and rel are Attribute Nodes
  • Google is a Text Node
Basic Terminology
  • document: the root object
  • Node: refers to an object (member of the DOM)
  • element: refers to an HTML element (member of the DOM)
  • nodeList: A collection (array-like) of elements

Basic DOM Manipulation

document.getElementById();

document.querySelector();

document.getElementsByTagName();

document.createElement();

*Note: plural verbs return lists...

Elements

Element.appendChild();

Element.remove();

Element.setAttribute( key, value );

Element.innerHTML

Element.textContent / Element.innerText*

*CodePen

Element.style

DOM Manipulation

  • Querying the DOM
  • Modifying properties, attributes and HTML classes
  • Creating, modifying and removing HTML Elements

Querying the DOM

Open your favorite web site (or click here) and navigate the DOM!

document.getElementById('hero'); 
// Selects the element with the specific id

document.getElementsByTagName('p'); 
// Selects all ellements that matching the tag

document.getElementsByClassName('btn'); 
// Selects all ellements that match CSS class

document.querySelector('h1'); 
// Selects the first h1 element that match selector

document.querySelectorAll('h2'); 
// Selects all the elements that match selector

Meet the "family"

const $el = document.querySelector('.container');

$el.parentNode;
$el.children;
$el.children.length;
$el.firstElementChild;
$el.lastElementChild;
$el.previousElementSibling;
$el.nextElementSibling;

Modifying properties and attributes

const $title = document.querySelector('h1');
const $img = document.querySelector('.img');

$title.id = 'my-ID';
$title.innerHTML = 'Hi all!';
$img.setAttribute("alt", "My image");

Fun with innerHTML on console

Open your favorite web site console and paste the code:

function YOLO() {

    const links = document.querySelectorAll("a");

    Array.from( links ).map( function(link){

        link.innerHTML = "YOLO" 

    });

}

YOLO();

Modifying HTML classes

const $el = document.querySelector( 'h1' );

$el.classList.add( 'myClass' );

$el.classList.remove( 'myClass' );

$el.classList.toggle( 'myClass' );

DOM Manipulation:

Create and append

Create a codepen (example)

const $paragraph = document.createElement( 'p' );

$paragraph.innerHTML = "Hello world!";

document.body.appendChild( $paragraph );

DOM Manipulation

Create and insertBefore

const $paragraph = document.createElement( 'p' );
$paragraph.innerHTML = "Hello world!";
document.body.appendChild( $paragraph );

const $title = document.createElement( 'h1' );
$title.innerHTML = "Welcome to JavaScript DOM Magic!";

// parentNode.insertBefore( Node, referenceNode );
document.body.insertBefore( $title, $paragraph );
CodePen Example

DOM Manipulation: Remove

const $el = document.querySelector( 'h1' );

$el.remove();

Quick Hands-on (Array.map + DOM)

https://bit.ly/2z7wSPI

Events & Capturing User Input

  • Events are actions that happen in time
  • They might be triggered from the user or the browser
  • We have many DOM events but most of the times we use mouse and keyboard events.

Click event


const $btn = document.querySelector('#MyButton');
function clickHandler( event ){
    console.log( event.type + ' got fired!' );
    console.log( this );
}
$btn.addEventListener( 'click', clickHandler );

CodePen
https://bit.ly/2O34Sq5

Meet the event object

  • event.type
  • event.target
  • event.preventDefault()
  • and a lot more depending the event type!

Click event hands on

this exercise
https://bit.ly/2DZADeZ

Most used events

  • click
  • mouseenter
  • mouseleave
  • mousedown
  • mouseup
  • mousemove
  • keydown
  • keyup
  • blur
  • focus

The KeyUp Event


const $input = document.querySelector( '#myInput' );

function keyUpHandler( event ){
    console.log( event.type + event.target.value );
}

$input.addEventListener( 'keyup', keyUpHandler );

codepen
https://bit.ly/2O5M6OB

Keyup Event Hands-On

Solve this exercise
https://bit.ly/2yfveKK

The mouseenter & mouseleave Events

$img.addEventListener( 'mouseenter', function( event ){

    console.log( event.type + event.target );

});

$img.addEventListener( 'mouseleave', function( event ){

    console.log( event.type + event.target );

});

codepen
https://bit.ly/2zTJwmp

Mouseenter and mouseleave event hands on

On Page Load

</body>
<script>
    function init( event ){

        console.log( event.type );
        // Main App Code

    }

    document.addEventListener('DOMContentLoaded', init );
</script>

codepen

Async

Handling Asynchronous code with Callbacks and Promises

Sync

console.log( 1 );
console.log( 2 );
console.log( 3 );

Will output:

1
2
3

Sync Example with Blocking Execution

console.log( 1 );
slowConnectionToDatabase();
console.log( "Rest of the code..." );

Execution

1
..........
"Rest of the code..."

Asynchronous Execution

console.log( 1 );
slowConnectionToDatabase();
console.log( "Rest of the code..." );

Execution

1
// slowConnectionToDatabase() starts execution but does not block the rest of the code...
"Rest of the code..."
......
// Result from slowConnectionToDatabase() comes back...

Example

console.log( 1 );

function displayTwo(){

    console.log( 2 );

}

setTimeout( displayTwo, 1000 );

console.log( 3 );

*Functions can be passed as arguments

Will output:

1
3
2

Let's dive deeper to see how to deal with asynchronous code...

Codepen

More on Callbacks:

JavaScript.info: Introduction to Callbacks

More on Promises:

JavaScript.info: Promise Basics
JavaScript Promises in Depth

JSON

(JavaScript Object Notation)

A Data Format

What is JSON

  • JSON is a simple & lightweight data-interchange format
  • It is easy for humans to read and write
  • It is easy for machines to parse and generate
  • It is completely language independent and a common web standard

JSON (Ελληνικά)

JSON allowed values

  • string
  • number
  • object
  • array
  • boolean
  • null
  • functions

An example

{
    "firstname": "Kostas",
    "lastname": "Minaidis",
    "address": {
        "city": "Athens",
        "street": "24, My Street",
    },
    "isMarried": false,
    "hobbies": [
        "books",
        "coding"
    ]
}

Convert an object to JSON

JSON.stringify

const me = {
    firstname: 'Kostas',
    lastname: 'Minaidis',
    hobbies: [
        "books", "coding"
    ]
};
const meJSON = JSON.stringify( me );
console.log( meJSON );
'{
    "firstname": "Kostas",
    "lastname": "Minaidis",
    "hobbies": [
        "books",
        "coding"
    ]
}'

Check: typeof me and typeof meJSON

Convert a JSON to object

JSON.parse

const meJSON = `{
"firstname": "Kostas",
"lastname": "Minaidis",
"address": {
    "city": "Athens",
    "street": "24, My Street"
},
"isMarried": false,
"hobbies": [ "books", "coding" ]
}`;
const me = JSON.parse(meJSON);
console.log(me);
{
firstname: 'Kostas',
lastname: 'Minaidis',
address: {
    city: 'Athens',
    street: '24, My Street',
},
isMarried: false,
hobbies: [
    "books", "coding"
]
}

Mention Template literals. Check: typeof me and typeof meJSON

JSON Examples Online

JSON Placeholder

https://bit.ly/2RfcGCR

  • 1) Open link and Copy the JSON data
  • 2) Open Browser console, declare a variable let json = ... and paste the copied JSON data as a value.
  • 3) Type json to get the object contents.
  • 4) Retrieve the object Post title...

Create and serve your
own sample JSON data


https://bit.ly/2NbOeiE

(Server emulation)

{
    "title": "On the Road",
    "author": "Jack Kerouac",
    "published": 1957,
    "isNovel": true,
    "characters": [ "Sal", "Dean" ]
}					        				

JSON Recap

Which of these is JSON?

remember JSON is slightly different from Javascript Objects

{
    "firstname": "John",
    "lastname": "Doe",
    "age": 35,
    "address": {
        "city": "Athens",
        "street": "my street",
        "number": 12
    }
}
{
    firstname: "John",
    lastname: "Doe",
    age: 35,
    address: {
        city: "Athens",
        street: "my street",
        number: 12
    }
}

JSON.parse

Takes a JSON string -> Returns a Javascript Object
let user = JSON.parse( "{'name':'nick','age':27}" );

console.log( user );
{
  name: "nick",
  age: 27
}

JSON.stringify

Takes a Javascript Object -> Returns a JSON string

It's the reverse of JSON.parse

JSON.stringify({ name: "nick", age: 27 });
"{'name':'nick','age':27}"

fetch()

  • fetch API provides a JavaScript interface for making HTTP requests
  • fetch() method provides an easy way to fetch resources asynchronously
  • fetch() is the modern alternative to the old and more complicated AJAX API
  • fetch() method returns a Promise

fetch example

Documentation

fetch('https://jsonplaceholder.typicode.com/posts/1/').then(function(response) {
    return response.json();
}).then(function(data) {
    console.log(data)
});

Fetch + APIs

OMDB API

Error Handling with Promises

CodePen

Let's Practice Promises!

https://bit.ly/2It2pPI (Promises)
https://bit.ly/2y3RLdl (Async/Await)

Prototype and Prototypal Inheritance

  • The new keyword and the Function constructor
  • What is this?
  • The road to ES6 Classes...

Understanding JavaScript’s Prototypal Inheritance

Time for a quick CodePen...

Understanding this

When a function is invoked an execution context is created which is like a record containing information about where the function was called from (call-stack), how the function was invoked, what parameters were passed, etc. One of the properties of this record is the this reference which will be used for the duration of that function's execution.

What this references is determined entirely
by the call-site where the function is called.

this in Global Context

console.log( this );

function run(){
    console.log( this );
}

Both functions run within the global (window) object.

window.console.log( this );
window.run();

In both cases, this references the window object (the functions' call-site).

this in Function Constructors

function Person( name ){
    console.log( this );
    this.name = name;
    this.show = function(){
        console.log( this.name );
    }
}
let person = new Person( "Kostas" );
person.show();

this is a reference to the Object created
by the Function constructor using the new keyword.

But...

Person( "Mary" );

this in ES6 Classes

class Person {
    constructor( name ){
        console.log( this );
        this.name = name;
    }
    show(){
        console.log( this.name );
    }
}
let person = new Person( "Kostas" );
person.show();

Since ES6 Classes are based on Function Constructors
and Objects, they behave in much the same way.

this in Object Methods

let person = {
    name: "Kostas",
    show: function(){
        console.log( this, this.name );
    }
}

When a function is called as a method of an object,
its this is set to the object the method is called on.

person.show();
// person { }, "Kostas"

this in Object Methods #2

let person = {
    name: "Kostas",
    show: function(){
        console.log( this, this.name );
    }
}
let other = {
    name: "Mary"
}
other.show = person.show;
other.show();
// other { }, "Mary"

REMEMBER: this is set to the object the method is called on.

this in DOM Event Handlers


let $button = document.querySelector("#clickme");

$button.addEventListener( "click", function(){

    console.log( this );

});

When a function is used as an event handler,
its this is set to the element the event fired from.

Explicit Binding:
bind(), apply(), call()

bind()

Bind this to any Object explicitly.


let person = { name: "Kostas" }
let $button = document.querySelector("#somebutton");
function handleClick(){ console.log( this ); }
$button.addEventListener( "click", handleClick.bind( person ) );

bind() #2


let person = { name: "Kostas" }

function show(){
    console.log( this.name );
}

show();

let personShow = show.bind( person );

personShow();

call(), apply()


let person = { 
    name: "Kostas",
    show: function( msg, end ){
        console.log( msg + this.name + end );
    } 
}
person.show( "Hello ", "!" );    // Hello Kostas!
let other = { name: "Mary" }

person.show.call( other, "Hi ", "!" );
person.show.apply( other, [ "Hi ", "!" ] );

this in Arrow Functions

Arrow functions don't provide their own this binding
It retains the this value of the enclosing lexical context.

let obj = {
    name: "Kostas",
    show: function(){ 
        setTimeout(function(){
         console.log( this.name );
        }, 2000)
    }
} 
obj.show();
/// undefined

Remember: setTimeout is called from window and thus
its inner function binds this to its context (that of the calling site).

let obj = {
    name: "Kostas",
    show: function(){ 
        window.setTimeout(function(){
         console.log( this.name );
        }, 2000)
    }
} 

How can we change this?

#1 Using bind()

let obj = {
    name: "Kostas",
    show: function(){ 
        window.setTimeout(function(){
         console.log( this.name );
        }.bind( this ), 2000)
    }
} 

#2 Using an Arrow Function

let obj = {
    name: "Kostas",
    show: function(){ 
        window.setTimeout(()=>{
         console.log( this.name );
        }, 2000)
    }
} 

Summary

Determining the this binding for an executing function
requires finding the direct call-site of that function.

Once examined, four rules can be applied
to the call-site, in this order of precedence:

- Called with new? Use the newly constructed object.

- Called with call or apply (or bind)? Use the specified object.

- Called with a context object owning the call? Use that context object.

- Default: undefined in strict mode, global object otherwise.

Recap...
...through some examples
W3Schools
Let's practice a bit...

Resources: