Created by Kostas Minaidis | Dec 2018
"Use let only for loop counters or only if you really need reassignment. Everywhere else, use const."
( Ανέλκυση )
"Hoisting is a JavaScript mechanism where variables and function declarations are moved to the top of their scope before code execution."
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."
Variable Hoisting: https://bit.ly/2GaIZBu
Function Hoisting: https://bit.ly/2L5vMZt
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.
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()
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()
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 ]
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!
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' ]
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
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 ]
let numbers = [ 2, 4, 8 ];
function modify( element ){
return element * element;
}
let results = numbers.forEach( modify ); // undefined
results = numbers.map( modify ); // [ 4, 16, 64 ]
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 ]
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 );
CodePen https://bit.ly/2RhnZL7
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
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.
All these objects are known as DOM Nodes.
Google
document.getElementById();
document.querySelector();
document.getElementsByTagName();
document.createElement();
*Note: plural verbs return lists...
Element.appendChild();
Element.remove();
Element.setAttribute( key, value );
Element.innerHTML
Element.textContent / Element.innerText*
Element.style
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
const $el = document.querySelector('.container');
$el.parentNode;
$el.children;
$el.children.length;
$el.firstElementChild;
$el.lastElementChild;
$el.previousElementSibling;
$el.nextElementSibling;
const $title = document.querySelector('h1');
const $img = document.querySelector('.img');
$title.id = 'my-ID';
$title.innerHTML = 'Hi all!';
$img.setAttribute("alt", "My image");
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();
const $el = document.querySelector( 'h1' );
$el.classList.add( 'myClass' );
$el.classList.remove( 'myClass' );
$el.classList.toggle( 'myClass' );
Create a codepen (example)
const $paragraph = document.createElement( 'p' );
$paragraph.innerHTML = "Hello world!";
document.body.appendChild( $paragraph );
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
const $el = document.querySelector( 'h1' );
$el.remove();
Quick Hands-on (Array.map + DOM)
https://bit.ly/2z7wSPI
const $btn = document.querySelector('#MyButton');
function clickHandler( event ){
console.log( event.type + ' got fired!' );
console.log( this );
}
$btn.addEventListener( 'click', clickHandler );
const $input = document.querySelector( '#myInput' );
function keyUpHandler( event ){
console.log( event.type + event.target.value );
}
$input.addEventListener( 'keyup', keyUpHandler );
Solve this exercise
https://bit.ly/2yfveKK
$img.addEventListener( 'mouseenter', function( event ){
console.log( event.type + event.target );
});
$img.addEventListener( 'mouseleave', function( event ){
console.log( event.type + event.target );
});
</body>
<script>
function init( event ){
console.log( event.type );
// Main App Code
}
document.addEventListener('DOMContentLoaded', init );
</script>
console.log( 1 );
console.log( 2 );
console.log( 3 );
Will output:
1
2
3
console.log( 1 );
slowConnectionToDatabase();
console.log( "Rest of the code..." );
Execution
1
..........
"Rest of the code..."
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...
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...
CodepenMore on Callbacks:
JavaScript.info: Introduction to CallbacksMore on Promises:
JavaScript.info: Promise Basics JavaScript Promises in DepthA Data Format
{
"firstname": "Kostas",
"lastname": "Minaidis",
"address": {
"city": "Athens",
"street": "24, My Street",
},
"isMarried": false,
"hobbies": [
"books",
"coding"
]
}
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
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
(Server emulation)
{
"title": "On the Road",
"author": "Jack Kerouac",
"published": 1957,
"isNovel": true,
"characters": [ "Sal", "Dean" ]
}
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
}
}
let user = JSON.parse( "{'name':'nick','age':27}" );
console.log( user );
{
name: "nick",
age: 27
}
JSON.stringify({ name: "nick", age: 27 });
"{'name':'nick','age':27}"
fetch('https://jsonplaceholder.typicode.com/posts/1/')
.then(function(response) { return response.json(); })
.then(function(data) { console.log(data) });
Understanding JavaScript’s Prototypal Inheritance
Time for a quick CodePen...
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.
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).
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" );
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.
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"
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.
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.
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 ) );
let person = { name: "Kostas" }
function show(){
console.log( this.name );
}
show();
let personShow = show.bind( person );
personShow();
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 ", "!" ] );
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)
}
}
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.