This document is written to help JavaScript developers to understand JavaScript's weird parts deeply and to prepare for interviews, the following resources was really helpful to write this document:
- No attribute is required for the
scriptelement. - The benefit of a separate file is browser caching.
- If
srcattribute is set, the content is ignored. - JavaScript interprets the line break as an implicit semicolon (not always).
- Execution Context
- Active execution contexts logically form the call stack.
- The top execution context on the call stack is the running execution context.
- The interpreter starts by the global execution context.
- Each invocation of a function, creates a new execution context appended to the top of the execution stack.
- Low level explanation
- Breaks into:
- LexicalEnvironment
- Is a lexical environment.
- Holds
letandconstdeclarations. Outerreference.
- VariableEnvironment.
- Is a lexical environment.
- Holds bindings created by
VariableStatementsandFunctionDeclarations. Outerreference.
- ThisBinding
- The value associated with the
thiskeyword.
- The value associated with the
- LexicalEnvironment
- Breaks into:
- High level explanation
- Can be devided to
- Creation phase
- Creates a variable object (aka. activation object)
- Variables declarations.
- Function declarations.
- Arguments.
- Create the scope chain.
- Determine the value of
this.
- Creates a variable object (aka. activation object)
- Execution phase
- The code is interpreted and executed.
- Creation phase
- Can be devided to
- Lexical Environment:
- Is a specification type.
- Like
LexicalEnvironmentandVariableEnvironment. - Consists of:
Environment Record: identifier bindings.outerreference to the outer lexical environment ornull.
- Context:
- Object-based.
- Is the value of
thiswhich is a reference to the object that owns the current executing code. - Determined by how a function is invoked.
- Scope:
- Function-bases.
- The region where the binding between a name (like variable name) to an entity is valid.
- Closure:
- A closure is a function that remembers its outer variables and can access them.
- Combination of a function and the lexical environment within which that function was declared
- The
closureis the function object itself. - Accessing variables outside of the immediate lexical scope creates a closure.
- Happens when we have a nested functions.
- JavaScript engines also may optimize, discard variables that are unused to save memory.
- A
Lexical Environmentobject lives in theheapas long as there is a function which may use it. And when there are none, it is cleared. - All functions in JavaScript are closures.
- The internal property
[[Environment]]of a function, refers to the outer lexical environment.
- IIFE
- Immediately-invoked function expressions.
- A design pattern used by most popular libraries to place all library code inside of a local scope.
- No global property is created for the function (anonymous function expression).
- All of the properties created inside of the function expression are scoped locally.
- Encapsulation, preserve the global namespace as any variables declared within the function body will be local to the closure but will still live throughout runtime.
- Benefits:
- Local scoping.
- Hoisting:
- Variable and function declarations are put into memory during the compile phase.
- Stays exactly where you typed it in your coding (not actually moved to the top).
- Only hoists declarations, not initializations.
- Declarations contribute to the
VariableEnvironmentwhen the execution scope is entered (^).
- Callbacks:
- A callback function is a function passed into another function as an argument.
- To be invoked inside the called function at some point, like after an asynchronous operation has completed.
- Callback Hell:
- Deeply nested callbacks.
- Called also
Pyramid of Doom.
- Promise
- Represents a value that either avaliable or will be avaliable in the future.
- Represents the completion or failure of an asynchronous operation, and its resulting value.
- Represents placeholder for the eventual results of a deferred (and possibly asynchronous) computation.
- The function passed with the arguments
resolveandrejectcalled theexecutorfunction. - Can either be:
- Fulfilled with a value.
- Rejected with a reason (error).
- Internal properties of promise instances:
[[PromiseResult]]the value or the error.[[PromiseState]]can be:pending,fulfilledorrejected.[[PromiseFulfillReactions]]queue forthenhandlers.[[PromiseRejectReactions]]queue forcatchhandlers.
- Methods:
Promise.all(iterable)Promise.race(iterable)Promise.resolve(value)returns a resolved promise with the given value.Promise.reject(reason)returns a rejected promise with the error.
- Pollyfills:
- Scripts that fill in the gap and add missing implementations for JavaScript or modifing the existing onces to support the modern standard.
- Use the same API.
- Like pollyfill for the
Function.prototype.bindwhich is not supported inIE8. - Or pollyfills for new methods in ES6 to be used in all browsers.
- Examples:
- Babel polyfill.
- polyfill.io.
- Fallback:
- Replaces the feature with:
- Simplified functionality
- Or Third-party plugin
- Or an error message
- Like if the browser doesn't support the video tag, replace it with flash plugin.
- Replaces the feature with:
Notes:
- Every function invocation has both a scope and a context associated with it.
- When an execution context is created its
LexicalEnvironmentandVariableEnvironmentcomponents initially have the same value. - The value of the
VariableEnvironmentcomponent never changes. - The value of the
LexicalEnvironmentcomponent may change during execution of code within an execution context like when entering a block. - Every run for a loop block has a separate
Lexical Environment.
Bind pollyfill:
if (!('bind' in Function.prototype)) {
Function.prototype.bind = function() {
var func = this;
var obj = arguments[0];
var params = Array.prototype.slice.call(arguments, 1);
return function() {
func.apply(obj, params.concat(Array.prototype.slice.call(arguments)));
}
}
}
Is a wrapper around a function that alters its behavior. Like caching the results or formatting them.
To implement them we need:
func.call(context, arg1, arg2…)callsfuncwith given context and arguments.func.apply(context, args)callsfuncwith given context and arguments passed as an array-like.func.bind(context, ...args)returns abound variantof functionfuncthat fixes the context and first arguments if given.
Note:
bindis really useful with:- Setting the context for
setTimeout's callback function. - Partial functions.
- Currying functions.
- Setting the context for
Creates a new function by fixing some parameters of the existing one.
- Translating a function from callable as
f(a, b, c)into callable asf(a)(b)(c). - To allow it to be called normally or get partials.
A wrapper function that passes everything it gets to another one.
function wrapper() {
return printArgs.apply(this, arguments);
}
Using a method from another object on our object.
Array.prototype.slice.call(arguments);
- Objects are associative arrays, stores key-value pairs.
- Keys must be
stringorsymbol, values can be anything. - To access a property, we can use:
- The dot notation:
obj.key - The square brackets notation
obj[key]
- The dot notation:
- An empty object can be created using:
- Using the object constructor
new Object(). - Using the object literal
{}.
- Using the object constructor
- To remove a property we can use the
deleteoperator. - Existence check
- Using
typeof,typeof obj.key !== 'undefined'. - Using
key in obj, better because if we have properties that storeundefined.
- Using
- The equality
==and strict equality===operators for objects work exactly the same. - Cloning and merging
- Shallow copy and merge
Object.assign(dest[, src1, src2, src3...]);Object.assign({}, user);Object.assignnot supported inIE, we can usefor..in.var clone = Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj));
- Deep copy
_.cloneDeep()from Lodash
- Shallow copy and merge
- There is other types of object other than the the
Object(aka.plain object):- Array
- Date
- Error
- To check if object literal:
Object.prototype.toString.call(obj) === '[object Object]' - Global object in browsers is
window. - Global object in Node.js is
global.
- Are regular functions.
- As convention it should be named with capital letter first, and not called without
new. - When the function is executed as
new User(), it does:- Create an empty object.
- Assign the
[[prototype]]of the created object toUser.prototype. - Execute the function and assign
thisto the created object. - Return the value of
thisor the value of thereturnstatement if the returned is an object.
- The default
prototypeis an object with the only propertyconstructorthat points back to the function itself. - If we replace the default prototype as a whole, then there will be no
constructorin it, unless we define manually. myObj.constructoris helpful, when we want to create another object from the same constructor that we don't know.var myObj2 = new myObj.constructor().- Any function can be run with
new. - To check if the function is called with
newor without we can usenew.targetinside it.
To allow creating an object with new or without:
function User(name) {
// Not called with new
if (!new.target) {
return new User(name);
}
this.name = name;
}
The new function() { … } pattern to encapsulate the creation of a single object:
var user = new function() {
this.name = 'Anas';
this.age = 'Ali';
}
- All objects have a hidden
[[Prototype]]property that’s either another object ornull. - That prototype object has a prototype of its own, and so on until an object is reached with
nullas its prototype. - Can be only one
[[Prototype]]no multiple inheritance. - The prototype is only used for reading properties.
- Write/delete operations work directly with the object.
delete obj.propertywill have no effect if the property is inherited (prototype chain).obj.property = 'value'will create a property on this object regardless if there is property calledpropertythat inherited.thisis not affected by prototypes at all.- No matter where the method is found: in an object or its prototype. In a method call,
thisis always the object before the dot. nullhas no prototype, and acts as the final link in this prototype chain.- Nearly all objects in JavaScript are instances of
Object. - To access the
[[Prototype]]:- Use the non-standard property
__proto__. - Use ES2015
Object.getPrototypeOf(),Object.setPrototypeOf().
- Use the non-standard property
__proto__is not the same as[[Prototype]]. That’s a getter/setter for it.__proto__is setter/getter for the[[Prototype]], defined inObject.prototype.- If an object that its prototype chain not end with the
Object, the__proto__will not be avaliable. - The
object instanceof contructoroperator examines the prototype chain for the check.var myObj = {}; console.log(myObj instanceof Object)istruebecausemyObj.__proto__ == Object.prototype
Creates an empty object with given proto as [[Prototype]] (can be null) and optional property descriptors.
Object.create(proto[, descriptors])
We can use Object.create to perform an object cloning:
var clone = Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj));
To create an empty object without a prototype
Object.create(null)
- JavaScript is prototype-based.
- There is no additional
[[Prototype]]in the chain aboveObject.prototype. Object.prototype.__proto__isnull.Array.prototype.__proto__ == Object.prototype.arr.__proto__.__proto__ == Object.prototype.- Modifying native prototypes is not recommended unless for polyfills.
- Functional class pattern.
- Create an object using
newand function constructor that has everything inside it. - Public properties are defined inside the constructor function.
- Public methods are defined inside the constructor function.
- Can have
privatemethods/variables that are not accessed from the outside.
- Create an object using
- Factory class pattern.
- Create an object by calling a function that returns a new object.
- Public properties are defined inside the returned object.
- Public methods are defined inside the returned object.
- Can have
privatemethods/variables that are not accessed from the outside.
- Prototype-based class pattern.
- Create an object using
newand function constructor. - Public properties are defined inside the constructor function.
- Public methods are defined inside the constructor's prototype property.
- Can't have
privatemethods/variables, prefixing with_is a convention to indicate that a member should not be used from the outside. - The constructor only initializes the current object state.
- More memory-efficient because all methods are shared between all objects.
- Is the best and mostly used.
- Create an object using
- Classical model
- Classes are represented by constructor functions.
- To inherit methods
Rabbit.prototype = Object.create(Animal.prototype);. - Reset the constructor property
Rabbit.prototype.constructor = Rabbit;. - To inherit properties
Animal.call(this, name);inside theRabbitconstructor function.
- Prototypal model
- Classes are just object litterals.
- To inherit
var rabbit = Objct.create(animal).
4 Ways to create a function:
- Function declaration:
- Hoisted, Processed before the code block is executed. They are visible everywhere in the block.
- Declarations contribute to the
VariableEnvironmentwhen the execution scope is entered.
- Function expression:
- Created when the execution flow reaches them.
- If they have a name assigned to them it is only visible by itself
var myFunc = function func() {...}
- Function constructor
new Function('a', 'b', 'return a + b'); - Arrow function.
- Do not have
this. - Do not have
arguments. - Can’t be called with
new. - Doesn't have their own
context, works in the current one.
- Do not have
Notes:
- Properties of a function:
- name: the name of the function
- length: number of arguments
- prototype
- prototype.constructor
- __proto__: Function.prototype
- Using
setTimeoutandsetIntervalfunctions. setTimeoutcan be used to implementsetInterval.- They are not a part of JavaScript specification (ES).
- Zero-timeout scheduling
setTimeout(...,0):- Is used to schedule the call as soon as possible, but after the current code is complete.
- To split CPU-hungry tasks into pieces, so that the script doesn’t hang.
- Let the browser do something else while the process is going on.
- Dynamically typed.
- There are data types, but variables are not bound to any of them.
- No
functiontype, they belong to the object type, buttypeoftreats them differently. - An old bug in JavaScript is
typeof null == 'object'. - Seven basic data type:
- number
- Serves both for integer and floating point numbers.
- There are special numeric values:
Infinity,-InfinityandNaN. - Maths is safe in JavaScript, will never stop with a fatal error.
NaNrepresents a computational error.1/0 == Infinity;-1/0 == -Infinity;"hello" / 2evaluated toNaN.
- string
- Encoded using UTF-16.
- Immutable, impossible to change a character.
- boolean
- null
- A standalone type that has a single value
null. - To indicate
emptyorunknown values.
- A standalone type that has a single value
- undefined
- A standalone type that has a single value
undefined. - To indicate
value not assigned - Only used for checks.
- A standalone type that has a single value
- object
- Not a primitive type.
- Collection of data.
- symbol
- For unique identifiers.
- number
typeof undefined // "undefined"
typeof 0 // "number"
typeof true // "boolean"
typeof "foo" // "string"
typeof Symbol("id") // "symbol"
typeof Math // "object"
typeof null // "object"
typeof alert // "function"
alertautomatically converts any value to a string.- Mathematical operations convert values to numbers.
- If condition expression converted to boolean.
String(null)become'null'String(undefined)become'undefined'String(false)become'false'String(true)become'false'String(10.1)become'10.1'
Number(null)become0Number(undefined)becomeNaNNumber(false)become0Number(true)become1Number('10.1')become10.1Number(' 10.1 \t\n ')become10.1Number(' 10.1z \t\n ')becomeNaNNumber(' \t\n ')become0Number('')become0
Notes:
- To convert a string into a number
- Trim whitespaces (spaces, tabs, new lines).
- An empty string becomes
0 - An error gives
NaN.
- Concatenation operation vs plus
1 + 2 + 'a' == '3a''a' + 1 + 2 == 'a12'
- Can convert to numbers with
+and-also.+'10' == 10-'10' == -10
False: values that are intuitively empty
Boolean(null)becomefalseBoolean(undefined)becomefalseBoolean('')becomefalseBoolean(0)becomefalseBoolean(NaN)becomefalse
True: otherwise
Boolean(' ')becometrueBoolean('0')becometrueBoolean('abc')becometrueBoolean(123)becometrue
Notes:
- happens in logical operations
- Methods of primitives:
- Primitives except
nullandundefinedprovide many helpful methods. - These methods work via temporary objects, but JavaScript engines are well tuned to optimize that internally.
- Like
'hello'.toUpperCase(),13.123.toFixed(2)
- Primitives except
new Number(10)returns anobjectnot primitive.
- All objects will become
trueif converted to boolean. - To convert to string, the
toString()methods will be used orvalueOf(). - To convert to number, the
valueOf()methods will be used ortoString().
- The
,comma operator.10,20 == 10- Evaluates both, returns the last one.
- Comparison operators return a logical value.
- Strings are compared letter-by-letter in the “dictionary” order.
- When values of different types are compared, they get converted to numbers (with the exclusion of a strict equality check).
- Values
nullandundefinedequal==each other and do not equal any other value.null == nullis true.null == undefinedis true.undefined == undefinedis true.
- An incomparable undefined, same as
NaN(always false):undefined > 0is false.undefined == 0is false.undefined < 0is false.undefined <= 0is false.
- Or (
||)- OR returns the first truthy value or the last one if no such value is found.
- And (
&&)- AND returns the first falsy value or the last value if none were found.
- The precedence of the
&&is higher than||.
(x > 0) && alert( 'Greater than zero!' );
null || 2 || undefined // 2
alert(1) || 2 || alert(3) // 2, since alert evaluated to undefined
1 && null && 2 // null
Multiple arguments
var sum = (a, b) => a + b;
Single argument
var double = n => n * 2;
Multiline arrow functions
var double = (n) => {
return n * 2;
};
- If a parameter is not provided, then its value becomes undefined.
- Default value for the parameter (not supported in IE)
function func(name = 'Anas'). - Checking for
undefinedbytypeof name === 'undefined'. - By using
||operator,name = name || 'Anas'.
Notes:
- A variable declared inside a function is only visible inside that function.
- The function is an object that its prototype points to
Function.prototype
hello.__proto__ == Function.prototype
- Applies to entire scripts or to individual functions.
- Strict mode changes semantics. Relying on those changes will cause mistakes and errors in browsers which don't implement strict mode.
'use strict'or"use strict"can be located at the top of a script or at the start of a function.- Only comments may appear above
"use strict". - There’s no way to cancel
use strict. - It is recommended.
Makes it impossible to accidentally create global variables.
mistypeVariable = 17;
Throws an error when trying to delete undeletable property.
delete Object.prototype;
Throws an error when trying to change unwritable property.
var person = {};
Object.defineProperty(person, 'age', {
value: 10,
writable: false
});
person.age = 13; // TypeError
Throws an error when the a function arguments names are not unique.
function func(a, a) {
console.log(a);
}
No longer possible to reference the global object through this inside a function.
function func() {
console.log(this); // undefined
}
func();
The caller, callee, and arguments properties may not be accessed on strict mode functions.
function func() {
console.log(arguments.callee); // TypeError
}
func();