- Design Patterns
- Scope & Closure
- Inheritance vs. Composition
- Asynchronous JavaScript
- Snippets
- ES6 Features
- Tips & Tricks
- Recursive Pattern Functions
- RegExp
- Generators
- Promises
JavaScript: Single Threaded
JavaScript is a single-threaded programming language, which means it has a single Call Stack. Therefore it can do one thing at a time.
Call Stack
The Call Stack is a data structure which records basically where in the program we are.
JavaScript Engine
A program which interprets and executes JavaScript code. A popular example of a JavaScript Engine is Google’s V8 engine.
Prototype Based
JavaScript is prototype-based with first-class functions.
First-class Functions
Meant that function can be stored in variables and then can be passed around.
High Order Functions
Means that function can take other function as input and return a function as output.
JavaScript is Multi-Paradigm
Means, you can program in with various coding styles like OOP or Functional or mix of both.
Performance ...
Start-up performance matters. A combination of slow parse, compile and execution times can be a real bottleneck for pages that wish to boot-up quickly.
- Compiled Languages are those who compiles the human understandable language (programming languages) to machine readable language before you run the program at build-time.
- compiled language applications can run directly once they are compiled
- Interpreted Languages are also human readable languages (programming languages) and needs a translation down to machine languages to get executed, but this translation is done at run-time.
- you always need the interpreter installed in your environment, before you run any interpreted language
- JIT (Just In Time)
- compile the code just before the execution
- The most important differences between a compiled and an interpreted language is; the compiled one takes a longer time to prepare itself to start executing, as it has to take care of lexing the entire codebase, making awesome optimizations etc. In the other hand an interpreted language starts executing in no time but doesn’t do any optimization of code.
- The modern JavaScript engines have JIT (they have a compiler).
- In JavaScript if a certain piece of code is run more than once, it’s called warm. When a function starts gets warmer, JIT sends it for compilation and saves the compiled code with a version. From next time if the same code is executed, it skips the translation and directly executes the compiled code. This speeds up the performance.
JavaScript, Compiled or Interpreted?
JavaScript execution looks complicated and kind of hybrid, but its more interpreted language.
- JavaScript code needs a tool (JS engine) installed in your machine (node, browser) to get executed. This is what interpreted languages want. Compiled language products are free to be executed directly.
- Hoisting etc are not like code modification. There is no intermediate code for that. It’s just the way JS interpreter handle things. (concept of execution context and not code modification)
- JIT is the only point which can raise questions on JavaScript being an interpreted language. But JIT is not a full fledged compiler, it also compiles just before the execution.
Software Program Development Stages
-
Compile Time (Build-Time)
the programming code is converted to executable code for machine
- Compile Time possible errors:
- Syntax errors
- Typechecking errors
- Compiler crashes (Rarely)
- Compile Time possible errors:
-
Execution Time (Run-Time)
the executable code is running by machine
- Run Time possible errors (referred as Exceptions):
- Division by zero
- Dereferencing a null pointer
- Running out of memory
- Trying to open a file that isn't there
- Trying find a web page and discovering that an alleged URL is not well formed
- Run Time possible errors (referred as Exceptions):
Notes
- As a general rule for building high-performance applications, everything that can be done at buildtime should not be done at runtime.
Inheritance is fundamentally a code reuse mechanism: A way for different kinds of objects to share code.
The way that you share code matters because if you get it wrong, it can create a lot of problems
- Classes inherit from classes and create subclass relationships: hierarchical class taxonomies.
- Instances are typically instantiated via constructor functions with the
new
keyword. - The ES6
class
keyword desugars to a constructor function. (type of a class is function) - In JavaScript, class inheritance is implemented on top of prototypal inheritance (that does not mean that it does the same thing)
Class Inheritance Issues
- The solution to all of following issues is to favor object composition over class inheritance.
- Class inheritance creates parent/child object taxonomies as a side-effect.
- The tight coupling problem (class inheritance is the tightest coupling available in OO Design Pattern)
- When you inherit from a class, you get everything, even if you don’t want it.
- The fragile base class problem
- Inflexible hierarchy problem
- The duplication by necessity problem (due to inflexible hierarchies, new use cases are often shoe-horned in by duplicating, rather than adapting existing code)
- The Gorilla/Banana problem (What you wanted was a banana, but what you got was a gorilla holding the banana, and the entire jungle)
class Human {
constructor(firstName, lastName) {
this.firstName = firstName,
this.lastName = lastName,
this.fullName = function() {
return `${this.firstName} ${this.lastName}`;
}
}
}
var person1 = new Human("Jane", "Doe");
var person2 = new Human("John", "Doe");
console.log(Human.prototype);
console.log(person1.__proto__);
console.log(Object.is(Human.prototype, person1.__proto__)); // true
Prototype ...
When a function is created in JavaScript, JavaScript engine adds a prototype property to the function. This prototype property is an object (called as prototype object) has a constructor property by default. constructor property points back to the function on which prototype object is a property. We can access the function’s prototype property using the syntax functionName.prototype.
Prototype object of the constructor function is shared among all the objects created using the constructor function.
function Human(firstName, lastName) {
this.firstName = firstName,
this.lastName = lastName,
this.fullName = function() {
return `${this.firstName} ${this.lastName}`;
}
}
var person1 = new Human("Jane", "Doe");
var person2 = new Human("John", "Doe");
console.log(Human.prototype);
console.log(person1.__proto__);
console.log(Object.is(Human.prototype, person1.__proto__)); // true
Instant Creation By: Object Linking to Other Objects - OLOO (
Object.create()
)
Object.create()
is a static method on the Object prototype.Object.create()
creates a new object with the prototype set to a certain object.Object.create()
is more natural to the prototype model (comparing with constructor model)
var Human = {
init(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
return this; // important to be able Object.create().init();
},
fullName() {
return `${this.firstName} ${this.lastName}`;
}
};
var person1 = Object.create(Human).init("Jane", "Doe");
console.log(Human.isPrototypeOf(person1)); // true
console.log(person1); // {firstName: "Jane", lastName: "Doe"}
console.log(person1.fullName()); // Jane Doe
var person2 = Object.create(Human, {
"firstName": {value: "John"},
"lastName": {value: "Doe"}
});
console.log(person2); // {firstName: "John", lastName: "Doe"}
Prototype Object
As prototype object is an object, we can attach properties and methods to the prototype object. Thus, enabling all the objects created using the constructor function to share those properties and methods.
New property can be added to the constructor function’s prototype property using either the dot notation or square bracket notation
Human.prototype.age = null;
- A prototype is a working object instance. Objects inherit directly from other objects.
- Instances may be composed from many different source objects, allowing for easy selective inheritance and a flat Prototype delegation hierarchy.
- Class taxonomies are NOT an automatic side-effect of prototypal OO
- Instances are typically instantiated via factory functions, object literals, or
Object.create()
.
Prototypal OO Types
- Concatenative Inheritance
- The process of inheriting features directly from one object to another by copying the source objects properties.
- Since ES6, this feature has a convenience utility in JavaScript called
Object.assign()
- Concatenative inheritance is the secret sauce that enables object composition in JavaScript
- jQuery, Underscore, Lodash, and the ES6
Object
, contain a utility that serves the purpose of concatenative inheritance.
- Prototype Delegation
- The ability to “share the behavior”, or delegate property lookup from the target object to some already existing object properties
- In JavaScript, an object may have a link to a prototype for delegation.
- If a property is not found on the object, the lookup is delegated to the delegate prototype,
which may have a link to its own delegate prototype,
and so on up the chain until you arrive at
Object.prototype
, which is the root delegate. - This is the prototype that gets hooked up when you attach to a
Constructor.prototype
and instantiate withnew
(You can also useObject.create()
) - You can even mix this technique with concatenation in order to flatten multiple prototypes to a single delegate, or extend the object instance after creation.
- Functional Inheritance
- In JavaScript, any function can create an object.
- When that function is NOT a constructor (NOR
class
), it’s called a factory function. - Functional inheritance works by producing an object from a factory, and extending the produced object by assigning properties to it directly (using concatenative inheritance)
- Most of the time, composition is achieved using factory functions: functions which exist to create object instances.
- Problem with constructor: Every object has its own instance of the function
- Problem with the prototype: Modifying a property using one object reflects the other object also
To solve above both problems, we can define all the object specific properties inside the constructor and all shared properties and methods insdie the prototype
Function Composition ...
Function composition is the process of combining two or more functions to produce a new function. Composing functions together is like snapping together a series of pipes for our data to flow through.
const compose = (...fns) => x => fns.reduceRight((v, f) => f(v), x);
the environment in which JavaScript code is executed
A. Global Execution Context (GEC)
Default execution context in which JS code start it’s execution when the file first loads in the browser.
- JavaScript engine wraps
executing code
withinexecution context
atexecution time
(run-time)- Execution Contexts is a JavaScript object
executionContextObj
- if the code is executing in strict mode value of
this
isundefined
otherwise it iswindow
object- Global execution context cannot be more than one because only one global environment is possible for JS code execution
- Global execution context's variables/functions are accessible by everywhere to everything in the code.
- By Global we mean code or variable not inside a function!
- Global Execution Context will create 3 things behind the scene for code:
1. Global Object (in browser iswindow
)
2. special variable calledthis
3. Outer environment (in Global there wouldn't be any outer env)
B. Functional Execution Context (FEC)
Functional execution context is defined as the context created by the execution of code inside a function.
- Each function has it’s own execution context (It can be more than one)
- Functional execution context have access to all the code of global execution context
- While executing global execution context code, if JS engine finds a function call, it creates a new functional execution context for that function
C. Eval
Execution context inside eval function
Execution context stack is a stack data structure to store all the execution stacks created while executing the JS code
- Global Execution Context is present by default in execution context stack and it is at the bottom of the stack.
- While executing global execution context code, if JS engines finds a function call, it creates Functional Execution Context of that function and pushes that function execution context on top of execution context stack.
- JS engine executes the function whose execution context is at the top of the execution context stack.
- Once all the code of the function is executed, JS engines pop’s out that function’s execution context and start’s executing the function which is below it.
Execution Context Creation Phases ...
JavaScript engine creates the execution context in the following two phases
When the code loads in the browser, JS engine will enter the compilation phase to create the execution objects. In the compilation phase JS engine will handle only the declarations, it does not bother about the values.
- Creates the Activation object or the variable object
Activation object is a special object in JS which contain all the variables, function arguments and inner functions declarations information.
- Creates the scope chain
Once the activation object gets created, JS engine initializes the scope chain which is a list of all the variables objects inside which the current function exists.
This also includes the variable object of global execution context.
Scope chain also contains the current function variable object. - Determines the value of this
After the scope chain, JavaScript engine initialize the value of
this
. - Setup Memory Space for Variables and Functions (Assignment)
As further there is no code, JS engine will now enter the execution phase and will scan the function again Here, JS engine will update the variable value and will execute the code.
- Assign Values
- Assign References to Functions
- Execute Code
Hoisting is Javascript’s default behavior of moving variable and function declarations to the top of their containing scope.
In real fact, just before the code is executed, it goes through a compiler, during this phase called lexing, scope gets defined and the variable and function declarations are moved to the top of their scope. If they are defined inside a function, they are moved to the top of this function, and if they are outside a function, to the top of the global scope.
Before your written code begin executed line by line, the JavaScript has already set aside memory space for the variables and functions you've created in the entire code. (so those variables and functions are existed in memory), however it doesn't know about their value and therefore will set a placeholder for them (default values).
Hoisting Only Declarations
JavaScript only hoists variable and function declarations (not Assignments, Initializations or Expressions)
The Declaration Comes Before the Assignment (Initializations | Expressions)
The declaration comes before the assignment: only variable and function declarations are hoisted,
while the assignments are left in place, just where they were written in the code.
Functions Before Variables
functions are hoisted first, and then variables.
Best Practice
- Declare all of your variables at the top of their scope (at the top of the global scope or at the top of the function scope)
- Put all your functions, if you can, also at the top of their scope.
- Try to initialize variables when you declare them. This will provide cleaner code and help avoid undefined variables.
In JavaScript functions are first class objects a function is a regular object of type function.
Functions Definition & Behavior 1
- Function Declaration (Hoisted)
function x10(num) { return num * 10 }
- Function Expression: functions that is assigned to a variable (Not Hoisted)
var x10Var = function x10(num) { return num * 10 }
x10 = (num) => { return num * 10 }
(function x10(num) { return num * 10 })() // IIFE
- Function Call
x10(3)
Arrow Functions
- Arrow functions mostly behave like function expressions.
- Arrow functions lexically bind the current
this
value.- Arrow functions do not create their own execution context, but takes it lexically (contrary to function expression or function declaration, which create own this depending on invocation)
arguments
object is not available in the arrow function
string
number
boolean
null
undefined
symbol
(A Symbol is a unique and immutable primitive value and may be used as the key of an Object property)
- A data that is not an object and has no methods.
- All primitives are immutable (cannot be changed).
- Except for null and undefined, all primitive values have object equivalents that wrap around the primitive values:
String
Object forstring
primitiveNumber
Object fornumber
primitiveBoolean
Object forboolean
primitiveSymbol
Object forsymbol
primitive
- The wrapper's
valueOf()
method returns the primitive value. - JavaScript automatically converts primitives to wrapper objects
Dates
Maths
Regular Expressions
Arrays
Functions
Objects
- Is a collection of properties
- Is a mapping between keys and values.
- useless when it comes to distinguishing between different kinds of objects - which includes arrays and nulls
typeof String() // string
typeof Number() // number
typeof Boolean() // boolean
typeof null // object
typeof undefined // undefined
typeof {} // object
typeof [] // object
typeof function(){} // function
- does not work with primitive values
- cross-window Issues
String() instanceof String; // false
Number() instanceof Number; // false
Boolean() instanceof Boolean; // false
null instanceof Object; // false
undefined instanceof Object; // false
/a/ instanceof RegExp; // true
{} instanceof Object; // true
[] instanceof Object; // true
[] instanceof Array; // true
new Date() instanceof Date; // true
function(){} instanceof Function; // true
- fastest method
- works with primitive values
new String().constructor === String; // true
new Number().constructor === Number; // true
new Boolean().constructor === Boolean; // true
- its slow but work in most of cases
- issue with different windows(as in popup window) in IE
Object.prototype.toString.call(new String()); // "[object String]"
Object.prototype.toString.call(new Number()); // "[object Number]"
Object.prototype.toString.call(new Boolean()); // "[object Boolean]"
Object.prototype.toString.call(null); // "[object Null]"
Object.prototype.toString.call(undefined); // "[object Undefined]"
Object.prototype.toString.call({}); // "[object Object]"
Object.prototype.toString.call([]); // "[object Array]"
Object.prototype.toString.call(new RegExp()); // "[object RegExp]"
Object.prototype.toString.call(new Date()); // "[object Date]"
Object.prototype.toString.call(function(){}); // "[object Function]"
Not Defined
vs. undefined
- If you call a variable before define it by name, JS engine throws
ReferenceError: <VAR> is not defined
- If you call a variable before assign it a value, JS engine automatically assign
undefined
value to it.undefined
is a value for variable and is different than not defined.