Skip to content

Instantly share code, notes, and snippets.

@technoglot
Last active April 30, 2019 19:25
Show Gist options
  • Save technoglot/a94f7ee0e2c6120f041103d79e1eb5d8 to your computer and use it in GitHub Desktop.
Save technoglot/a94f7ee0e2c6120f041103d79e1eb5d8 to your computer and use it in GitHub Desktop.
Collection of JavaScript concepts explained.

What is hoisting in JS?

Basically, when JavaScript compiles all of your code, all variable declarations using var are hoisted/lifted to the top of their functional/local scope (if declared inside a function) or to the top of their global scope (if declared outside of a function) regardless of where the actual declaration has been made. This is what we mean by “hoisting”.

Functions declarations are also hoisted, but these go to the very top, so will sit above all of the variable declarations.

However, a key thing to note is that the only thing that gets moved to the top is the variable declarations , not the actual value given to the variable.

All declarations (var, let, const, function, function*, class) are "hoisted" in JavaScript.

Note

  • Variables and constants declared with let or const are not hoisted!
  • JavaScript only hoists declarations, not initializations. If a variable is declared and initialized after using it, the value will be undefined.
  • If a developer doesn't understand hoisting, programs may contain bugs (errors).
  • In JavaScript, an undeclared variable is assigned the value undefined at execution and is also of type undefined.
  • In JavaScript, a ReferenceError is thrown when trying to access a previously undeclared variable.
  • Undeclared variables do not exist until code assigning them value is executed. Therefore, assigning a value to an undeclared variable implicitly creates it as a global variable when the assignment is executed. This means that, all undeclared variables are global variables.
  • The scope of a variable declared with the keyword var is its current execution context.
  • By enabling strict mode, we opt into a restricted variant of JavaScript that will not tolerate the usage of variables before they are declared.
  'use strict';
	
   // OR
   "use strict";
  • Variables declared with let and const remain uninitialized at the beginning of execution whilst variables declared with var are initialized with a value of undefined.
  • A constant variable must be both declared and initialized before use.
  • Function expressions, however are not hoisted.
  • Function declarations are hoisted over variable declarations but not over variable assignments.
  • JavaScript class declarations are hoisted. However, they remain uninitialized until evaluation. This effectively means that you have to declare a class before you can use it.

Running our code in strict mode:

  1. Eliminates some silent JavaScript errors by changing them to explicit throw errors which will be spit out by the interpreter.
  2. Fixes mistakes that make it difficult for JavaScript engines to perform optimizations.
  3. Prohibits some syntax likely to be defined in future versions of JavaScript.

Recommendations

  • To avoid bugs, always declare all variables at the beginning of every scope. Since this is how JavaScript interprets the code, it is always a good rule.
  • JavaScript in strict mode does not allow variables to be used if they are not declared.

Examples

function catName(name) {
	console.log("My cat's name is " + name);
}
catName("Tigger");
/*
The result of the code above is: "My cat's name is Tigger"
*/

-----

console.log(num); // Returns undefined 
var num;
num = 6;

-----

num = 6;
console.log(num); // returns 6
var num;

-----

var x = 1; // Initialize x
console.log(x + " " + y); // '1 undefined'
var y = 2; // Initialize y
// The above example is implicitly understood as this: 
var x; // Declare x
var y; // Declare y
// End of the hoisting.
x = 1; // Initialize x
console.log(x + " " + y); // '1 undefined'
y = 2; // Initialize y

What is an IIFE(pronounced ‘iffy’) and what does it stand for?

An IIFE (Immediately Invoked Function Expression) is a JavaScript function that runs as soon as it is defined. It is a design pattern which is also known as a Self-Executing Anonymous Function and contains two major parts.

  1. The first is the anonymous function with lexical scope enclosed within the Grouping Operator (). This prevents accessing variables within the IIFE idiom as well as polluting the global scope.
  2. The second part creates the immediately executing function expression () through which the JavaScript engine will directly interpret the function.

Assigning the IIFE to a variable stores the function's return value, not the function definition itself.

An Immediately Invoked Function Expression is a good way at protecting the scope of your function and the variables within it. The term ‘scope’ might also be a bit fancier than it needs to be — it basically just means where it can be accessed from.

Where can you find IIFEs?

You will often (but not always) see IIFEs used in frameworks/libraries such as jQuery. They will often wrap all of the code for their framework/library inside of an IIFE in order to protect the scope of its variables and also to make sure that everything is executed without the user having to do anything.

Examples

var result = (function () {
	var name = "Barry"; 
	return name; 
})(); 
// Immediately creates the output: 
result; // "Barry"

//FUNCTION DECLARATION
function doSomething(){
	// ...do something...
};

//FUNCTION EXPRESSION
var doSomething = function(){
	// ...do something...
};

By wrapping our function in parenthesis, we tell the parser to parse our JavaScript as a function expression, and not a function declaration. This allows our code to compile without any errors!

A pair of parenthesis immediately after our function declaration will immediately invoke the function.

//IIFE
(function(){
	// ...do something...
})();

Without the parenthesis, the function is never invoked, and thus the function definition is returned instead.

speak();
// 'hello'
speak;
// function speak(){
// console.log('hello');
// }

Why would we use an IIFE instead of just creating a function and invoking it right afterwards? Privacy.

In JavaScript, variables are scoped to their containing function. This means that they can’t be accessed outside of the function.

(function(){
	var superSecret = 195;
})()
console.log(superSecret);
// Uncaught ReferenceError: superSecret is not defined

Why would you just create a named function and invoke it? That would create the same result? Yes, but with consequences: Creating a named function pollutes the global name space. It also means the named function is hanging around. Our IIFE isn’t named and therefor can’t accidentally be called later — avoiding any potential security implications.

It is worth pointing out that you can easily pass arguments into the IIFE as well.

var foo = "foo";
(function (innerFoo) {
	// Outputs: "foo"
	console.log(innerFoo);
})(foo);

Intro

JavaScript variables can belong to the local or global scope. Global variables can be made local (private) with closures.

Global vs Local Variables

  • In a web page, global variables belong to the window object. Global variables can be used (and changed) by all scripts in the page (and in the window).
  • A local variable can only be used inside the function where it is defined. It is hidden from other functions and other scripting code.
  • Global and local variables with the same name are different variables. Modifying one, does not modify the other.
  • Variables created without the keyword var, are always global, even if they are created inside a function.

Variable Lifetime

  1. Global variables live as long as your application (your window / your web page) lives.
  2. Local variables have short lives. They are created when the function is invoked, and deleted when the function is finished.

What is a Lexical Scope?

A lexical scope or static scope in JavaScript refers to the accessibility of the variables, functions, and objects based on their physical location in the source code.

Execution Context

An execution context is an abstract environment where the JavaScript code is evaluated and executed. When the global code is executed, it’s executed inside the global execution context, and the function code is executed inside the function execution context.

There can only be one currently running execution context (Because JavaScript is single threaded language), which is managed by a stack data structure known as Execution Stack or Call Stack.

Lexical Environment

Every time the JavaScript engine creates an execution context to execute the function or global code, it also creates a new lexical environment to store the variable defined in that function during the execution of that function.

A lexical environment is a data structure that holds identifier-variable mapping. (here identifier refers to the name of variables/functions, and the variable is the reference to actual object [including function type object] or primitive value).

A Lexical Environment has two components: (1) the environment record and (2) a reference to the outer environment.

  • The environment record is the actual place where the variable and function declarations are stored.
  • The reference to the outer environment means it has access to its outer (parent) lexical environment. This component is the most important in order to understand how closures work.

JavaScript Nested Functions

All functions have access to the global scope. In fact, in JavaScript, all functions have access to the scope "above" them.

JavaScript supports nested functions. Nested functions have access to the scope "above" them.

Closures in JS

A closure is a function having access to the parent scope, even after the parent function has closed. A closure is the combination of a function and the lexical environment within which that function was declared. This environment consists of any local variables that were in-scope at the time the closure was created.

Closures are useful because they let you associate some data (the lexical environment) with a function that operates on that data.

With closures it is possible to emulate private methods. Private methods aren't just useful for restricting access to code: they also provide a powerful way of managing your global namespace, keeping non-essential methods from cluttering up the public interface to your code.

Examples

function init() {
	var name = 'Mozilla'; // name is a local variable created by init
	function displayName() { // displayName() is the inner function, a closure
	alert(name); // use variable declared in the parent function 
	}
displayName(); 
}
init();

init() creates a local variable called name and a function called displayName(). The displayName() function is an inner function that is defined inside init() and is only available within the body of the init() function. The displayName() function has no local variables of its own. However, because inner functions have access to the variables of outer functions, displayName() can access the variable name declared in the parent function, init(). However, the same local variables in displayName() will be used if they exist.

This is an example of lexical scoping, which describes how a parser resolves variable names when functions are nested. The word "lexical" refers to the fact that lexical scoping uses the location where a variable is declared within the source code to determine where that variable is available. Nested functions have access to variables declared in their outer scope.

function makeAdder(x) {
	return function(y) {
		return x + y;
	};
}

var add5 = makeAdder(5);
var add10 = makeAdder(10);

console.log(add5(2)); // 7
console.log(add10(2)); // 12

In this example, we have defined a function makeAdder(x), which takes a single argument, x, and returns a new function. The function it returns takes a single argument, y, and returns the sum of x and y.

In essence, makeAdder is a function factory — it creates functions which can add a specific value to their argument. In the above example we use our function factory to create two new functions — one that adds 5 to its argument, and one that adds 10.

add5 and add10 are both closures. They share the same function body definition, but store different lexical environments.

Practical applications of closures

var counter = (function() {
	var privateCounter = 0;
	function changeBy(val) {
	privateCounter += val;
	}
	return {
		increment: function() {
			changeBy(1);
		},
		decrement: function() {
			changeBy(-1);
		},
		value: function() {
			return privateCounter;
		}
	}; 
})();

console.log(counter.value()); // logs 0
counter.increment();
counter.increment();
console.log(counter.value()); // logs 2
counter.decrement();
console.log(counter.value()); // logs 1

Here, though, we create a single lexical environment that is shared by three functions: counter.increment, counter.decrement, and counter.value.

The shared lexical environment is created in the body of an anonymous function, which is executed as soon as it has been defined. The lexical environment contains two private items: a variable called privateCounter and a function called changeBy. Neither of these private items can be accessed directly from outside the anonymous function. Instead, they must be accessed by the three public functions that are returned from the anonymous wrapper.

Those three public functions are closures that share the same environment. Thanks to JavaScript's lexical scoping, they each have access to the privateCounter variable and changeBy function.

Closure Scope Chain

For every closure we have three scopes:-

  • Local Scope (Own scope)
  • Outer Functions Scope
  • Global Scope
// global scope
var e = 10;
function sum(a){
	return function(b){
		return function(c){
			// outer functions scope
			return function(d){
				// local scope
				return a + b + c + d + e;
			}
		}
	}
}
console.log(sum(1)(2)(3)(4)); // log 20

In the example above, we have a series of nested functions all of which have access to the outer functions' scope scope, but which mistakenly guess only for their immediate outer function scope. In this context, we can say all closures have access to all outer function scopes within which they were declared.

To fully understand Higher Order Functions, you first have to understand what Functional Programming is and the concept of First-Class Functions.

What is Functional Programming?

Functional Programming is a form of programming in which you can pass functions as parameters to other functions and also return them as values. In functional programming, we think and code in terms of functions. Pure functional programming is side-effect free.

What are First-Class Functions?

In JavaScript or any other functional programming languages functions are objects, a special type of objects. They are Function objects.

To prove functions are objects in JavaScript, we could do something like this:

// We can add properties to functions like we do with objects
	greeting.lang = 'English';
// Prints 'English'
	console.log(greeting.lang);

Note — While this is perfectly valid in JavaScript, this is considered a harmful practice. You should not add random properties to the function objects, use an object if you have to.

Everything you can do with other types like object, string, or number, you can do with functions. You can:

  • pass them as parameters to other functions (callbacks),
  • assign them to variables and
  • pass them around etc.

This is why functions in JavaScript are known as First-Class Functions.

Assigning Functions to Variables

We can assign functions to variables in JavaScript. For example:

const square = function(x) {
	return x * x;
}
// prints 25
square(5); 

We can also pass them around. For example:
const foo = square;
// prints 36
foo(6);

Passing Functions as Parameters

We can pass functions as parameters to other functions. For example:

function formalGreeting() {
	console.log("How are you?");
}

function casualGreeting() {
	console.log("What's up?");
}

function greet(type, greetFormal, greetCasual) {
	if(type === 'formal') {
		greetFormal();
	} else if(type === 'casual') {
		greetCasual();
	}
}

// prints 'What's up?'
greet('casual', formalGreeting, casualGreeting);

What are Higher-Order Functions?

A Higher-Order function is a function that receives a function as an argument or returns the function as output.

For example, Array.prototype.map, Array.prototype.filter and Array.prototype.reduce are some of the Higher-Order functions built into the language.

Creating Our own Higher-Order Function

const formatCurrency = function(currencySymbol, decimalSeparator) {
	return function( value ) {
		const wholePart = Math.trunc( value / 100 );
		let fractionalPart = value % 100;
		if ( fractionalPart < 10 ) {
			fractionalPart = '0' + fractionalPart;
		}
	return `{currencySymbol}${wholePart}${decimalSeparator}${fractionalPart}`;
	}
}

getLabel = formatCurrency( '$', '.' );
getLabel( 1999 )
//"$19.99"

getLabel( 2499 )
//"$24.99"

formatCurrency returns a function with a fixed currency symbol and decimal separator.

We pass the formatter a value, then format this value by extracting its whole part and the fractional part. Notice that I used the ES6 math extension trunc to truncate the result.

The return value of this function is constructed by a template literal, concatenating the currency symbol, the whole part, the decimal separator, and the fractional part.

The currency symbol and the decimal separator are not passed to the returned function, they are fixed values.

We can pass integer values to the getLabel function, and we get a formatted representation back.

Therefore, the formatCurrency higher order function returned a usable formatter function.

What are classes in JS?

Classes are in fact "special functions", and just as you can define function expressions and function declarations, the class syntax has two components: class expressions and class declarations.

Class declarations

One way to define a class is using a class declaration. To declare a class, you use the class keyword with the name of the class.

class Rectangle {
	constructor(height, width) {
		this.height = height;
		this.width = width;
	}
}

An important difference between function declarations and class declarations is that function declarations are hoisted and class declarations are not. You first need to declare your class and then access it, otherwise code like the following will throw a ReferenceError:

const p = new Rectangle(); // ReferenceError
class Rectangle {
	//some code
}

Class expression

A class expression is another way to define a class. Class expressions can be named or unnamed. The name given to a named class expression is local to the class's body. (it can be retrieved through the class's (not an instance's) name property, though).

// unnamed
let Rectangle = class {
	constructor(height, width) {
		this.height = height;
		this.width = width;
	}
};
console.log(Rectangle.name);
// output: "Rectangle"
// named
let Rectangle = class Rectangle2 {
	constructor(height, width) {
		this.height = height;
		this.width = width;
	}
};
console.log(Rectangle.name);
// output: "Rectangle2"

Note: Class expressions are subject to the same hoisting restrictions as described in the Class declarations section.

Class body and method definitions

The body of a class is the part that is in curly brackets {}. This is where you define class members, such as methods or constructor.

Constructor

The constructor method is a special method for creating and initializing an object created with a class. There can only be one special method with the name "constructor" in a class. A SyntaxError will be thrown if the class contains more than one occurrence of a constructor method.

A constructor can use the super keyword to call the constructor of the super class.

Static methods

The static keyword defines a static method for a class. Static methods are called without instantiating their class and cannot be called through a class instance. Static methods are often used to create utility functions for an application.

class Point {
	constructor(x, y) {
		this.x = x;
		this.y = y;
	}

	static distance(a, b) {
		const dx = a.x - b.x;
		const dy = a.y - b.y;
		return Math.hypot(dx, dy);
	}
}

const p1 = new Point(5, 5);
const p2 = new Point(10, 10);

console.log(Point.distance(p1, p2)); // 7.0710678118654755

Code within the class body's syntactic boundary is always executed in strict mode.

Instance Properties

Instance properties must be defined inside of class methods:

class Rectangle {
	constructor(height, width) { 
		this.height = height;
		this.width = width;
	}
}

Static class-side properties and prototype data properties must be defined outside of the ClassBody declaration:

Rectangle.staticWidth = 20;
Rectangle.prototype.prototypeWidth = 25;

Field declarations

Public and private field declarations are an experimental feature (stage 3) proposed at TC39, the JavaScript standards committee.

Public field declarations

With the JavaScript field declaration syntax, the above example can be written as:

class Rectangle {
	height = 0;
	width;
	
	constructor(height, width) { 
		this.height = height;
		this.width = width;
	}
}

By declaring fields up-front, class definitions become more self-documenting, and the fields are always present.

As seen above, the fields can be declared with or without a default value.

Private field declarations

Using private fields, the definition can be refined as below.

class Rectangle {
	#height = 0;
	#width;

	constructor(height, width) { 
		this.#height = height;
		this.#width = width;
	}
}

It's an error to reference private fields from outside of the class; they can only be read or written within the class body. Private fields cannot be created later through assigning to them, the way that normal properties can.

Sub classing with extends

The extends keyword is used in class declarations or class expressions to create a class as a child of another class.

class Animal { 
	constructor(name) {
		this.name = name;
	}

	speak() {
		console.log(this.name + ' makes a noise.');
	}
}

class Dog extends Animal {
	constructor(name) {
	super(name); // call the super class constructor and pass in the name parameter
	}

	speak() {
		console.log(this.name + ' barks.');
	}
}

let d = new Dog('Mitzie');
d.speak(); // Mitzie barks.

If there is a constructor present in the subclass, it needs to first call super() before using "this".

One may also extend traditional function-based "classes":

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

Animal.prototype.speak = function () {
	console.log(this.name + ' makes a noise.');
}

class Dog extends Animal {
	speak() {
		console.log(this.name + ' barks.');
	}
}

let d = new Dog('Mitzie');
d.speak(); // Mitzie barks.

Note: that classes cannot extend regular (non-constructible) objects. If you want to inherit from a regular object, you can instead use Object.setPrototypeOf().

Super class calls with super

The super keyword is used to call corresponding methods of super class. This is one advantage over prototype-based inheritance.

Object Oriented JavaScript - Classes (super and extends)

class Animal {
	constructor(name, weight) {
		this.name = name;
		this.weight = weight;
	}

	eat() {
		return `${this.name} is eating!`;
	}

	sleep() {
		return `${this.name} is going to sleep!`;
	}

	wakeUp() {
		return `${this.name} is waking up!`;
	}
}

class Gorilla extends Animal {
	constructor(name, weight) {
		super(name, weight);
	}

	climbTrees() {
		return `${this.name} is climbing trees!`;
	}
	
	poundChest() {
		return `${this.name} is pounding its chest!`;
	}
	
	showVigour() {
		return `${super.eat()} ${this.poundChest()}`;
	}
	
	dailyRoutine() {
		return `${super.wakeUp()} ${this.poundChest()} ${super.eat()} ${super.sleep()}`;
	}
}

function display(content) {
	console.log(content);
}

const gorilla = new Gorilla('George', '160Kg');
display(gorilla.poundChest());
display(gorilla.sleep());
display(gorilla.showVigour());
display(gorilla.dailyRoutine());

// OUTPUT:
// George is pounding its chest!
// George is going to sleep!
// George is eating! George is pounding its chest!
// George is waking up! George is pounding its chest! George is eating! George is going to sleep!

Code explanation

The above code has 2 JavaScript classes namely Animal and Gorilla. The Gorilla class is a subclass or child class of Animal and it uses the extends keyword to set itself as a subclass.

However, the super keyword has been used in two different ways. In Gorilla’s constructor (line 23 of code) super is used as a “function”. Whereas, Gorilla’s showVigour() (line 35) and dailyRoutine()(line 39) methods have used super as an “object”.

The super keyword is used in two ways because of the following reasons:

  1. In line 23, the super keyword is used as a “function” which calls the parent class Animal with the parameters passed to Gorilla. This is a key step to be carried out in order to make sure that Gorilla is an instance of Animal.
  2. In line numbers 35 and 39, super is used as an “object” which refers to an Animal instance (parent class). The super keyword here is used to call the methods of the parent class Animal explicitly.

Traditional JavaScript Classes - Prototypal Inheritance

function Animal(name, weight) {
	this.name = name;
	this.weight = weight;
}

Animal.prototype.eat = function() {
	return `${this.name} is eating!`;
}

Animal.prototype.sleep = function() {
	return `${this.name} is going to sleep!`;
}

Animal.prototype.wakeUp = function() {
	return `${this.name} is waking up!`;
}

function Gorilla(name, weight) {
	Animal.call(this, name, weight);
}

Gorilla.prototype = Object.create(Animal.prototype);
Gorilla.prototype.constructor = Gorilla;

Gorilla.prototype.climbTrees = function () {
	return `${this.name} is climbing trees!`;
}

Gorilla.prototype.poundChest = function() {
	return `${this.name} is pounding its chest!`;
}

Gorilla.prototype.showVigour = function () {
	return `${Animal.prototype.eat.call(this)} ${this.poundChest()}`;
}

Gorilla.prototype.dailyRoutine = function() {
	return `${Animal.prototype.wakeUp.call(this)} ${this.poundChest()} ${Animal.prototype.eat.call(this)} ${Animal.prototype.sleep.call(this)}`;
}

function display(content) {
	console.log(content);
}

var gorilla = new Gorilla('George', '160Kg');
display(gorilla.poundChest());
display(gorilla.sleep());
display(gorilla.showVigour());
display(gorilla.dailyRoutine());

// OUTPUT:
// George is pounding its chest!
// George is going to sleep!
// George is eating! George is pounding its chest!
// George is waking up! George is pounding its chest! George is eating! George is going to sleep!
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment