JavaScript Learning 101

This repository is for my daily learning of JavaScript basic featues, codesnippet, new featues, and so on. So I can track my learning progress and can recall of this materials If I forget.

Callback function

A callback function is a function passed as an argument to another function. This technique allows a function to call another function. A callback function can run after another function has finished.

Why do we need Callback Functions?

JavaScript runs code sequentially in top-down order. However, there are some cases that code runs (or must run) after something else happens and also not sequentially. This is called asynchronous programming.

Callbacks make sure that a function is not going to run before a task is completed but will run right after the task has completed. It helps us develop asynchronous JavaScript code and keeps us safe from problems and errors.

In JavaScript, the way to create a callback function is to pass it as a parameter to another function, and then to call it back right after something has happened or some task is completed. Let’s see how…

Example:

const message = function() {  
    console.log("This message is shown after 3 seconds");
}
 
setTimeout(message, 3000);

Callback as an Anonymous Function

setTimeout(function() {  
    console.log("This message is shown after 3 seconds");
}, 3000);

Callback as an Arrow Function

setTimeout(() => { 
    console.log("This message is shown after 3 seconds");
}, 3000);

Events

<button id="callback-btn">Click here</button>
document.queryselector("#callback-btn")
    .addEventListener("click", function() {    
      console.log("User has clicked on the button!");
});

Higher-order function

In Javascript, functions can be assigned to variables in the same way that strings or arrays can. They can be passed into other functions as parameters or returned from them as well.

A “higher-order function” is a function that accepts functions as parameters and/or returns a function.

On the other side, the functions that use only primitives or objects as arguments, and only return primitives or objects are named first-order functions.

A. Assign functions to variables:

// Assign to variables
const hiFunction = function() { 
  return 'Hello!' 
};
hiFunction(); // => 'Hello!'

B. Use functions as arguments to other functions:

// Use as arguments
function iUseFunction(func) {
  return func();
}
iUseFunction(function () { return 42 }); // => 42

In the above examples, iUseFunction() is higher-order because it accepts a function as an argument.

C. And even return functions from functions:

// Return function from function
function iReturnFunction() {
  return function() { return 42 };
}
const myFunc = iReturnFunction();
myFunc(); // => 42

"The functions that use other functions as arguments or return functions are named higher-order functions."

In the previous examples, iUseFunction() is higher-order because it accepts a function as an argument. Also iReturnFunction() is a higher-order function because it returns another function.

In the previous examples, hiFunction() is a first-order function since it simply returns a number.

So, in JavaScript a function can be either first-order or higher-order.

Event loop

The event loop is an architectural design pattern that allows code to run asynchronously on the JavaScript engines and makes it possible for code to execute blocking instruction first and when available execute asynchronous function.

The event loop utilizes there major components, call stack, event queue and web apis. It manages function calls in a way that asynchronous events would go into the event queue and will be called only after the call stack is empty.

Promise vs Observables

JavaScript is a single-threaded, non-blocking, and asynchronous concurrent language. That means that JavaScript’s engine doesn’t sit and wait for statements to finish. Instead, it moves to the next statement.

Promises and Observables have a completely different approach to dealing with async code.

One value vs. multiple values

The biggest difference is that Promises won’t change their value once they have been fulfilled. They can only emit (reject, resolve) a single value. On the other hand, observables can emit multiple results. The subscriber will be receiving results until the observer is completed or unsubscribed from.

Observable subscriptions are cancellable; promises aren’t

Once you start a promise, you can’t cancel it. The callback passed to the Promise constructor will be responsible for resolving or rejecting the promise. The subscriber is passive; once fired, it can just react to the result.

Observables are less passive. Once a subscriber is created, it can opt out of the observer at any time. That makes them useful in scenarios where we are no longer interested in the response.

Eager vs. lazy execution

There is a difference in how Observables and Promises are executed. Promises are executed eagerly whilst Observables are executed lazily.

Eagar: The Promise callback will execute right away at the constructor level.

Lazy: The Producer function will only trigger after there is a subscription created for that Observable. Otherwise, it will stay idle.

Runtime execution The ES Promises, once resolved, will queue the callback in the microtask queue. That means they will be executed after the current macro task has been completed.

With Observables, you can fine-tune the runtime execution using schedulers. A scheduler controls when a subscription starts and when notifications are delivered.

Apply vs Bind vs Call

All three of the call, bind, and apply methods set the this argument to the function. The call and apply methods set this to a function and call the function. The bind method will only set this to a function. We will need to separately invoke the function.

call() and apply() are identical in functionality, the only difference is that call() accepts a list of arguments; whereas, apply() accepts a single array of arguments. The bind method binds the this value to the function and returns a new function. However, we still need to separately invoke the returned function.

Promise.all() vs Promise.allSettled()

Promise.all() method takes an iterable of promises as input and returns a single Promise. This returned promise fulfills when all of the input's promises fulfill (including when an empty iterable is passed), with an array of the fulfillment values. It rejects when any of the input's promises rejects, with this first rejection reason. The Promise.allSettled() method takes an iterable of promises as input and returns a single Promise. This returned promise fulfills when all of the input's promises settle (including when an empty iterable is passed), with an array of objects that describe the outcome of each promise.

Hoisting

Hoisting in JavaScript is a behavior in which a function or a variable can be used before declaration.

// using test before declaring
console.log(test);   // undefined
var test;

The above program works and the output will be undefined. The above program behaves as

// using test before declaring
var test;
console.log(test); // undefined

Hoisting is a kind of default behavior in which all the declarations either variable declaration or function declaration are moved at the top of the scope just before executing the program's code. However, it can be considered an advantage because all functions and variable declarations are placed to the top of their scope no matter where they are all declared anywhere in the whole program, even regardless of whether they are declared global or local.

JSONP

JSON with Padding, or JSONP for short, is a technique that allows developers to get around browsers' same-origin policies by exploiting the nature of the <script> element. The policy prohibits reading any responses made by websites with origins other than those currently in use. In addition, the policy allows for the submission of requests but not the reading of them. It's a method of retrieving data that avoids the cross-domain problem. To accomplish this, the script tag is needed.

The padding refers the function call that wraps the JSON object.

{
  "id": 1,
  "room": "main bedroom",
  "items": [ "bed", "chest of drawers" ]
}

JSONP:

jsonpcallback({
  "id": 1,
  "room": "main bedroom",
  "items": [ "bed", "chest of drawers" ]
});

The above 2 codes just has one difference which is the function call jsonpcallback() that is wrapping the JSON.

JSONP is used to bypass the Cross-Domain Request boundaries, under which the scripts that run on one domain are restricted to access the data from another page on different domain.

Prototype

The prototype is an object that is associated with every functions and objects by default in JavaScript, where function's prototype property is accessible and modifiable and object's prototype property (aka attribute) is not visible. Every function includes prototype object by default.

function ===========> prototype

The prototype object is special type of enumerable object to which additional properties can be attached to it which will be shared across all the instances of it's constructor function.

So, use prototype property of a function in the above example in order to have age properties across all the objects as shown below.

function Employee() {
    this.name = 'John';
    this.gender = 'M';
}

Employee.prototype.age = 29;

var empObj1 = new Employee();
alert(empObj1.age); // 29

var empObj2 = new Employee();
alert(empObj2.age); // 29

Every object which is created using literal syntax or constructor syntax with the new keyword, includes proto property that points to prototype object of a function that created this object.

function Employee() {
    this.name = 'Jehn';
    this.gender = 'F';
}

var empObj = new Employee();

console.log(Employee.prototype); // object
console.log(empObj.prototype); // undefined
console.log(empObj.__proto__); // object

console.log(typeof Employee.prototype); // object
console.log(typeof empObj.__proto__); // object

console.log(Employee.prototype === empObj.__proto__ ); // true

The prototype property is special type of enumerable object which cannot be iterate using for..in or foreach loop.

Constructor vs Prototype

The constructor is a function that is used to create an object, while the prototype is an object that contains properties and methods that are inherited by objects created from a constructor.

function Person(name) {
  this.name = name;
}

Person.prototype.sayHello = function() {
  console.log(`Hello, my name is ${this.name}.`);
}

const person = new Person('John');
person.sayHello(); // Hello, my name is John.

In the example above, we have a constructor function that takes a name parameter and assigns it to the name property of the object. We also have a prototype method called sayHello, which prints a message to the console.

When we create a new Person object using the constructor function, we can call the sayHello method on it, and it will print the message with the name that we passed into the constructor.

The person object can use the sayHello method because it’s inherited from the prototype of the Person constructor function.

Object.freeze()

The Object.freeze() method freezes an object. Freezing an object prevents extensions and makes existing properties non-writable and non-configurable. A frozen object can no longer be changed: new properties cannot be added, existing properties cannot be removed, their enumerability, configurability, writability, or value cannot be changed, and the object's prototype cannot be re-assigned. freeze() returns the same object that was passed in.

const obj = {
 prop: 42
};

Object.freeze(obj);

obj.prop = 33;
// Throws an error in strict mode

console.log(obj.prop);
// expected output: 42

When you should not use Arrow Functions

  1. Object methods

When you call cat.jumps, the number of lives does not decrease. It is because this is not bound to anything, and will inherit the value of this from its parent scope.

var cat = {
  lives: 9,
  jumps: () => {
    this.lives--;
  }
}
  1. Callback functions with dynamic context

If you need your context to be dynamic, arrow functions are not the right choice. Take a look at this event handler below:

var button = document.getElementById('press');
button.addEventListener('click', () => {
  this.classList.toggle('on');
});

If we click the button, we would get a TypeError. It is because this is not bound to the button, but instead bound to its parent scope.

  1. When it makes your code less readable

It is worth taking into consideration the variety of syntax we covered earlier. With regular functions, people know what to expect. With arrow functions, it may be hard to decipher what you are looking at straightaway.

Immediately-invoked function expression (IIFE)

// IIFE
const message = (function(name) {
  return 'Hello ' + name + '!';
})('World');