Javascript is a light weight object oriented language which dominates most of the Web development. It has a simple syntax, supports Event-based programming and allowed to dynamically generate content on web pages. All the major browsers slowly started providing support to run JavaScript efficiently by developing their own JavaScript engines. Different browsers use different JS engine to execute Javascript code, for example Microsoft Edge uses Chakra, Firefox uses SpiderMonkey and Google Chrome uses V8 JS Engine. Hence the same Javascript code could behave differently on different browsers. The release of Node JS, a runtime environment for JavaScript, allowed to run Javascript outside the browser on the server side and supported asynchronous programming, which has truly revolutionized Javascript. Node JS was developed in 2009 using Chrome V8 engine of the Chromium Project. It allowed JavaScript to run everywhere, unifying all of web application development around a single programming language, rather having a different language for server-side and client-side scripts. This allowed companies to develop applications entirely with JavaScript as a full-stack single programming language which made it easier to debug and save costs in the overall development process. The popularity of JavaScript led to the creation of several libraries and frameworks that have made application development much more efficient with better performance. Libraries like React JS, jQuery, D3.js etc and Frameworks like Angular, Ember JS, and Vue JS provide optimal performance and features to build large applications.
ECMAScript is the Javascript standard meant to ensure the interoperability of web pages across different web browsers. It is commonly used for client-side scripting on WebPages and it is increasingly being used for server side applications using Node JS. The First edition of ECMA-262 was published in 1997 and subsequently many editions followed. The ECMAScript 2015 also known as ECMAScript 6 (ES6) is the second major revision of JavaScript language since ES5 which was standardized in 2009. Recently ECMAScript 2020 (ES11) features were released in 2020.
Javascript uses semicolon to separate each statement. The semicolon is only obligatory when there are two or more statements on the same line. The semicolon in JavaScript is used to separate statements, but it can be omitted if the statement is followed by a line break or there’s only one statement in a {block}.
Data Types
There are six basic data types in JavaScript which can be divided into three main categories: primitive (or primary), composite (or reference), and special data types. String, Number, and Boolean are primitive data types. Object, Array, and Function (which are all types of objects) are composite data types. Whereas Undefined and Null are special data types.
Primitive data types can hold only one value at a time. The string data type is used to represent textual data. The number data type is used to represent positive or negative numbers with or without decimal place. The Boolean data type can hold only two values: true or false. If a variable has been declared, but has not been assigned any value, then its value is undefined. A null value means that there is no value for the variable.
The composite data types can hold collections of values and more complex entities. The object is a complex data type which allows to store collections of data. An object contains properties, defined as a key-value pair. A property key (name) is always a string, but the value can be any data type, like strings, numbers, booleans, or complex data types like arrays, function and other objects.
An array is a type of object used for storing multiple values in single variable. Array can contain data of any data type for example numbers, strings, booleans, functions, objects, and other arrays. In Javascript we can define any collection by ending with a comma as below.
namelist = ['John', 'Tyler', 'Mike',]
The function is callable object that executes a block of code. The typeof operator can be used to find out what type of data a variable or operand contains. It can be used with or without parentheses (typeof(x) or typeof x).
var greeting = function(){ return "Hello World!"; } console.log(typeof greeting) console.log(greeting());
JavaScript is a loosely typed language and does not require a data type to be declared. We can assign any literal values to a variable, e.g., string, integer, float, boolean, etc.
Variable Types: Var, Let Const
Var variable scope is either global or local in ES6. When a var variable is declared outside a function, its scope is global, while when its declared inside a function, the scope is local. When the var variable is declared outside it is added to the property list of the global object. The global object is window on the web browser and global on Node JS. The var variable can be redeclared without any issue. When var variable is defined it is immediately initialized to undefined. Var variable can be redeclared without any issue. Variables can be declared and initialized to a value without the var keyword, but this is not recommended.
Let variables are declared with a block-scope, were the variable is only accessible within the block. Let variable are not initialized to any value on declaration, and are not attached to the global object. Redeclaring a variable using the let keyword will cause an error. A temporal dead zone of a variable declared using the let keyword starts from the block until the initialization is evaluated. Hence once a let variable is defined a storage space is allocated for the variable but it is not initialized. Referencing uninitialized let variable causes a ReferenceError. Let variables are mutable, hence its value can be updated.
Const variable similar to let does not allow to redefine the same variable name again and does not allow variables to be accessed outside its declared (block/method) scope. Const variable unlike let variable are immutable. The const keyword creates a read-only reference to a value which cannot be reassigned but the value can be changed. In order to prevent any changes to the object itself, Object.freeze() is used for shallow freeze.
It is recommended to avoid using var as much as possible, prefer const or let if possible. It is important to note that variable names are case-sensitive in JavaScript. Javascript allows to use a variable and function before declaring it. The Javascript compiler moves all the declarations of variables and functions at the top so that there will not be any error, called as Hoisting.
Strict Mode (ES 5)
Strict mode eliminates some JavaScript silent errors by changing them to throw errors. For example accessing global object or updating non-existing property etc. Strict mode applies to entire scripts or to individual functions. It does not apply to block statements enclosed in {} braces and applying it in such way does not do anything. In order to invoke strict mode for an entire script, the statement "use strict"; is added before any other statements. Similarly to invoke strict mode for a function (and any inner functions), the statement "use strict"; is added within the function before any other statements.
// Entire script strict mode "use strict"; x = 3.14; // throws error as x is not declared // Function script strict mode function strictMode() { 'use strict'; function nested() { return 'Nested function also in strict mode !'; } return "This is a strict mode function! " + nested(); }
Operators
Javascript supports standard Logical operators i.e. AND (&&), OR (||) and NOT (!), standard Arithmetic (+, -, *, **, /, %, ++, --), Assignment (=, +=, -=, *=, /=, %=, <<=, >>=, >>>=, &=, ^=, |=, **=) and Ternary (:?) Operator. Javascript supports standard Comparison operators !=, >, <, >= and <=.
The == comparison operator compares the equality of two operands without considering type, while the === operator compares equality of two operands including their type. The == operator compares for equality after doing any necessary type conversions. The === operator does not do any conversion, hence if two values are not the same type === will simply return false. It is recommended to always use === and !== operators instead of their evil twins == and != operators.
'' == '0' // false 0 == '' // true 0 == '0' // true 0 == false // true 0 === false // false "" == false // true "" === false // false "" == 0 // true "" === 0 // false '0' == 0 // true '0' === 0 // false '17' == 17 // true '17' === 17 // false [1,2] == '1,2' // true [1,2] === '1,2' // false null == undefined // true null === undefined // false "abc" == new String("abc") // true, == operator evaluates String object using its toString() or valueOf() to primitive type "abc" === new String("abc") // false, as primitive string and String object reference are of different types
String
String is a primitive data type in JavaScript. Any string must be enclosed in single or double quotation marks. A string can also be treated like zero index based character array. A string is immutable in JavaScript, it can be concatenated using plus (+) operator in JavaScript.
var str = 'Hello World'; str[0] // H str.length // 11
The characters of the string can be accessed by index.
for(var ch of str) { console.log(ch); }
The eval() is a global function in JavaScript that evaluates a specified string as JavaScript code and executes it.
eval("console.log('this is executed by eval()')");
The String object is used to represent and manipulate a sequence of characters. It allows to check the length, and methods to get location of substring using indexOf() method, get character at a location charAt(), extracting substrings with the substring() method and allow many other string operations. The replace() method is used to replace part of the string with another string. The search() method takes a regular expression and returns the first index on which the expression was found. The String() method can be used to convert any other data type value to string values.
String(25) // 25 is converted to string "25" String([]) // [] is converted to empty string "" String(null) // null is converted to string "null" String(true) // true is converted to string "true" String({}) // {} is converted to string(similar to calling toString() on a object)
Number
The Number is a primitive data type used for positive or negative integer, float, binary, octal, hexadecimal, and exponential values in JavaScript. The Number type in JavaScript is double-precision 64 bit binary format like double in Java. The integer value is accurate up to 15 digits in Javascript, and float values can keep precision upto 17 decimal places. Arithmetic operations on floating-point numbers in JavaScript are not always accurate.
Number is a primitive wrapper object used to represent and manipulate numbers. The Number() is a constructor function in JavaScript that converts values of other types to numbers. The Number constructor contains constants and methods for working with numbers. The new operator with Number() returns an object which contains constants and methods for working with numbers. JavaScript treats primitive values as objects, so all the properties and methods are applicable to both literal numbers and number objects.
var i = Number('100'); typeof(i); // returns number var f = new Number('10.5'); typeof(f); // returns object
Below are examples of using the Number() constructor to convert any other data type value to numeric values.
Number("25") // "25" string is converted to number 25 Number("") // "" string is converted to 0 Number([]) // [] is converted to 0 Number(null) // null is converted to 0 Number(true) // true is converted to 1 Number(false) // false is converted to 0 Number("Test") // "Test" could not be converted to number
It is very important to note that new Number() creates object instance, while Number function returns a number type which is same as any primtive number.
3 === new Number(3) // false, new keyword creates a Number object instance. Value does not equal to object reference. 3 === Number(3) // true, Number function returns a value type typeof new Number(3) // object typeof Number(3) // number typeof 3 // number
The parseInt(), parseFloat() functions parses a string argument and returns an integer or floating point number respectively.
The global NaN property is a value representing Not-A-Number. NaN is a special numeric value that is not equal to itself.
NaN === NaN // false const notANumber = 3 * "a" // NaN notANumber == notANumber // false notANumber === notANumber // false
The corresponding global isNaN() function is used to determine whether a value is NaN or not. The isNaN() global function attempts to coerce it's argument before checking if it's NaN. It is recommended to avoid global isNAN function.
isNaN("name") // true isNaN("1") // false
ECMAScript 6 introduced a method for checking NaN, Number.isNaN.
Number.isNaN(NaN) // true Number.isNaN("name") // false
Boolean
The Boolean object is an object wrapper for a boolean value. The Boolean converts the passed value to boolean value. If the value is omitted or is 0, -0, null, false, NaN, undefined, or the empty string (""), the object has an initial value of false. All other values, including any object, an empty array ([]), or the string "false", create an object with an initial value of true.
Boolean(null) // false Boolean(0) // false Boolean(25) // true Boolean([]) // true Boolean({}) // true Boolean("Yeah !") // true
Math
Math is a built-in object that has properties and methods for mathematical constants and functions. It’s not a function object. There are various Math built in functions available in Javascript, few of them below.
The Math.random() function returns a floating-point, pseudo-random number in the range 0 to less than 1 with approximately uniform distribution over that range.
console.log(Math.random()); // number from 0 to <1
The Math.floor() function returns the largest integer less than or equal to a given number.
console.log(Math.floor(5.95)); // output: 5 console.log(Math.floor(5.05)); // output: 5
The Math.max() function returns the largest of the zero or more numbers given as input parameters, or NaN if parameter is not a number.
console.log(Math.max(1, 3, 2)); const array1 = [1, 3, 2]; console.log(Math.max(...array1));
Object
Objects are same as variables in JavaScript, the only difference is that an object holds multiple values in terms of properties and methods. Objects are collections of key value pairs in Javascript. Object can be created either using Object Literal/Initializer Syntax or using the Object() Constructor with the new keyword. An object can be created with figure brackets {…} with an optional list of properties. A property is a “key: value” pair, where key is a string (also called a “property name”), and value can be anything. Alternatively, properties and methods can be declared using the dot notation .property-name or using the square brackets ["property-name"], as shown below. Object in JavaScript is passed by reference from one function to another.
const person = { // object literal syntax name: 'Jarvis', walk: function() {}, talk() { console.log("talking.."); } } person.talk(); person['name'] = 'John'; person.name = ''; var newperson = new Object(); // Object() constructor newperson.firstName = "James"; newperson["lastName"] = "Bond"; newperson.age = 25; newperson.getFullName = function () { return this.firstName + ' ' + this.lastName; };
The for in loop allows to enumerate properties of the object.
for(var prop in person){ console.log(prop); // access property name console.log(person[prop]); // access property value };
The Object class store various keyed collections and more complex entities. The Object() constructor is used to create Objects. Object class a number of static methods which allows to get or set properties to any Object.
Object.assign(target, source): It copies the values of all enumerable properties from one or more source objects to a target object, and returns the modified target object.
Object.fromEntries(value): It transforms a list of key-value pairs into an object.
Object.freeze(): It freezes the object, were new properties cannot be added, existing properties cannot be removed, existing properties configurability, or writability cannot be altered and values of existing properties cannot be changed. It also prevents its prototype from being changed.
Object.entries(value): It returns an array containing all of the [key, value] pairs of a given object's own enumerable string properties.
Object.is(value1, value2): Compares if two values are the same value.
Object.seal(object1): It seals the object, preventing new properties from being added to it and marking all existing properties as non-configurable.
Object.values(object1): It returns an array of a specified object's property values in order in which they are defined.
Object.keys(object1): It returns an array containing all the property names for the specified object.
Object.getOwnPropertyDescriptor(object1, 'property1'): It returns an object describing the configuration of a specific property on a given object. The configuration for specified property of an object includes its value, boolean config such as configurable, enumerable and writable.
Object.defineProperty(object1, 'property1', { .. }): It defines a new property directly on an object, or modifies an existing property on an object, and returns the object.
const object1 = {}; Object.defineProperty(object1, 'property1', { value: 87, writable: false }); object1.property1 = 909; console.log(object1.property1); // value still remains 87, throws error in strict mode
Object.getPrototypeOf(object1): It returns the value of internal prototype of the specified object.
Object.setPrototypeOf(object, prototype): It sets the internal of a specified object to another object or null.
Memory management in JavaScript is performed automatically and invisibly to us. JavaScript automatically allocates memory when objects are created and frees it when they are not used anymore (garbage collection). Objects are retained in memory while they are reachable.
Date
JavaScript provides Date object to work with date & time including days, months, years, hours, minutes, seconds and milliseconds.
// current date as function Date(); // current date as an object var currentDate = new Date(); // Date specifying milliseconds var date2 = new Date(1000); var date3 = new Date("3 august 2021"); var date4 = new Date("2021 3 August"); var date5 = new Date("3 august 2021 20:21:44"); // Date using valid separator var date6 = new Date("August-2021-3"); date.toDateString(); date.toLocaleDateString(); date.toGMTString(); date.toISOString(); date.toLocaleString(); date.toString('YYYY-MM-dd'); if(date1 > date2) { console.log(date1 + ' is greater than ' + date2); } // convert valid date string into unix epoch milliseconds Date.parse("8/2/2021");
The toJSON() method converts a Date object into a string, formatted as a JSON date.
const date = new Date('August 13, 1973 23:15:30 UTC'); console.log(date.toJSON());
Arrays
An array is a special type of variable in Javascript, which can store multiple values. Every value is associated with numeric index starting with 0. JavaScript array can store multiple element of different data types. It is not required to store value of same data type in an array. An array can be initialized using a constructor using new keyword. Array has several methods such as forEach(), join(), map(), push(), reduce(), slice() etc.
var stringArray1 = ["one", "two", "three"]; var numericArray1 = [1, 2, 3, 4]; var mixedArray1 = [1, "two", "three", 4]; var stringArray = new Array(); stringArray[0] = "one"; var numericArray = new Array(1); numericArray[0] = 1; var mixedArray = new Array(1, "two", 3, "four");
Array class is a global object that is used in the construction of arrays. It has following handy static methods.
Array.from(): It creates a new, shallow-copied Array instance from an array-like or iterable object.
Array.isArray(): It determines whether the passed value is an Array.
Array.of(): It creates a new Array instance from a variable number of arguments, regardless of number or type of the arguments.
JSON
JSON stands for Javascript Object Notation. It is derived from JavaScript object syntax, but it is entirely text-based. JSON is a natural choice for data format in Javascript. JSON data is normally accessed in Javascript through dot notation. We can also use the square bracket syntax to access data from JSON.
Javascript provides a JSON object which contains methods for parsing JavaScript Object Notation (JSON) and converting values into JSON.
JSON.stringify(original, replacer): It converts a JavaScript object or value to a JSON string. By default, all instances of undefined are replaced with null. The optional replacer function allows to replace specific values. On other hand when the optional replacer array is specified then the JSON string only includes the specified properties from the replacer array.
console.log(JSON.stringify([new Number(3), new String('false'), new Boolean(false)])); // expected output: "[3,"false",false]" console.log(JSON.stringify(new Date(2021, 0, 2, 15, 4, 5))); // expected output: ""2021-01-02T15:04:05.000Z"" function replacer(key, value) { if (typeof value === 'string') { // Filter properties return undefined; } return value; } var foo = {company: 'SpaceX', model: 'box', week: 45, transport: 'jet', month: 7}; // Replacer Function JSON.stringify(foo, replacer); // Replacer Array JSON.stringify(foo, ['week', 'month']);
JSON.parse(text, reviver): It parses a JSON string to construct the JavaScript object or value. An optional reviver function can be provided to perform a transformation on the resulting object before it is returned. JSON.parse() does not allow trailing commas and single quotes.
JSON.parse('{}'); // {} JSON.parse('true'); // true JSON.parse('"foo"'); // "foo" JSON.parse('[1, 5, "false"]'); // [1, 5, "false"] JSON.parse('null'); // null const jsonString = '[{"name":"John","score":51},{"name":"Jack","score":17}]'; var data = JSON.parse(jsonString, function reviver(key, value) { return key === 'name' ? value.toUpperCase() : value; }); console.log(data);
Regular Expressions
A regular expression in Javascript is a type of object. The regular expression can be either constructed using the RegExp constructor or written as a literal value by enclosing a pattern in forward slash (/) characters. The RegExp constructor uses the pattern as a normal string, so the regular rules apply for backslashes. The pattern between slash characters, requires a backslash be added before any forward slash in order to be the part of the pattern. Also backslashes that aren’t part of special character codes (like \n) will be preserved, rather than ignored as they are in strings, and change the meaning of the pattern. Some characters, such as question marks and plus signs, have special meanings in regular expressions and must be preceded by a backslash if they are meant to represent the character itself. In the below example, the pattern matches for "abc" and "eighteen+".
let reg1 = new RegExp("abc"); let reg2 = /abc/; let eighteenPlus = /eighteen\+/;
The syntax of regular expression is /pattern/modifiers. Modifiers are used to perform change the match criteria. The modifier (i) performs case-insensitive matching, the (g) modifier finds all the matches rather than stopping after the first match and the (m) modifier performs multiline matching. Modifiers are optional in regular expression. The pattern is used to determine the match in the string.
In a regular expression pattern, putting a set of characters between square brackets makes that part of the expression match any of the characters between the brackets. Within square brackets, a hyphen (-) between two characters can be used to indicate a range of characters, e.g. [0-9] covers all of them and matches any digit. Below are common character groups with their own built-in shortcuts.
Character Group | Description |
---|---|
\d | Any digit character |
\w | An alphanumeric character (“word character”) |
\s | Any whitespace character (space, tab, newline, and similar) |
\D | A character that is not a digit |
\W | A nonalphanumeric character |
\S | A nonwhitespace character |
. | Any character except for newline |
In order to invert the set of characters, i.e. to match any character except the ones in the set, a caret (^) character after the opening bracket e.g. /[^01]/. To match an element which may be repeated more than once, a plus sign (+) after the element in the regular expression, e.g. /\d+/ matches one or more digit characters. Similarly, the element with a star (*) after it allows the pattern to match element zero or more times. A question mark (?) in a pattern indicates the part od pattern may occur zero times or one time. To indicate that a pattern should occur a precise number of times, braces are used with number of exact occurrences, e.g. {4}. It is also possible to specify a range this way, with {2,4} means the element must occur at least twice and at most four times. Open-ended ranges can also be specified using braces by omitting the number after the comma, e.g. {5,} means five or more times. In order to use an operator such as * or + on more than one element at a time, each such part a regular expression is enclosed in parentheses representing as a single element .e.g /boo+(hoo+)+/i.
The RegExp object is a regular expression object with predefined properties and methods. The exec() and test() methods of RegExp object are used to match the regular expression patterns. The test() method takes the string and returns a Boolean indicating whether the string contains a match of the pattern in the expression.
console.log(/abc/.test("abcde")); // true console.log(/[01239]/.test("in 1992")); // true console.log(/[0-9]/.test("in 1992")); // true
The execute() method returns an object containing the details of the match or else null if there is no match. The matched object has an index property which tells us where in the string the successful match begins. Most of the matched object is an array of strings, whose first element is the string that was matched.
let match = /\d+/.exec("one two 100"); console.log(match); // ["100"] console.log(match.index); // 8
Regular expression objects have properties source and lastIndex. The source property contains the string that expression was created from, while the lastIndex property controls the position from where the next match will start. The regular expression must have the global (g) or sticky (y) option enabled, and the match must happen through the exec method. If the match was successful, the call to exec automatically updates the lastIndex property to point after the match. If no match was found, lastIndex is set back to zero, which is also the value it has in a newly constructed regular expression object.
let pattern = /y/g; pattern.lastIndex = 3; let match = pattern.exec("xyzzy"); console.log(match.index); // 4 console.log(pattern.lastIndex); // 5 let global = /abc/g; console.log(global.exec("xyz abc")); // ["abc"] let sticky = /abc/y; console.log(sticky.exec("xyz abc")); // null
Implicit Coercion
Javascript's implicit coercion simply refers to Javascript attempting to coerce an unexpected value type to the expected type. So we can pass a string where it expects a number, an object where it expects a string etc, and it will try to convert it to the right type. It is recommended to avoid this feature.
Whenever we pass a string as an operand in a numeric expression involving either of arithmetic operators (-, *, /, %), the number's conversion process is similar to calling the in-built Number function on the value. Hence any string containing only numeric characters will be converted to its number equivalent, but a string containing a non-numeric character returns NaN.
3 * "3" // 3 * 3 3 * Number("3") // 3 * 3
The + operator unlike other mathematical operators, performs both Mathematical addition as well as String concatenation. When a string is an operand of the + operator, Javascript instead of converting the string to a Number, converts the number to a string.
1 + 2 // 3 1 + "2" // "12" 1 + 2 + "1" // "31" 1 + "2" + 1 // "121"
Every javascript Object inherits a toString method, that is called whenever an Object is to be converted to a string. The return value of the toString method is used for such operations as string concatenation and mathematical expressions.
const foo = {} foo.toString() // [object Object] const baz = { toString: () => "I'm object baz" } baz + "!" // "I'm object baz!"
When it's a mathematical expression, Javascript will attempt to convert the return value to a number, if it's not.
const bar = { toString: () => "2" } 2 + bar // "22" 2 * bar // 4
The toString method inherited from Object works similar to the join method without arguments for arrays.
[1,2,3].toString() // "1,2,3" [1,2,3].join() // "1,2,3" "me" + [1,2,3] // "me1,2,3" 4 + [1,2,3] // "41,2,3" 4 * [1,2,3] // NaN
When we pass an array where it expects a string, Javascript concatenates the return value of the toString method with the second operand. If it expects a number, it attempts to convert the return value to a number.
4 * [] // 0 4 * Number([].toString()) 4 * Number("") 4 * 0 4 / [2] // 2 4 / Number([2].toString()) 4 / Number("2") 4 / 2
Every Javascript value can be coerced into either true or false. Coercion into boolean true means the value is truthy, while coercion into boolean false means the value is falsy. Javascript returns false when them the value of variable is 0, -0, null, undefined, "", NAN and of course for false. In all other cases Javascript returns boolean expression value as true.
if (-1) // truthy if ("0") // truthy if ({}) // truthy const counter = 2 if (counter) // truthy
Javascript converts boolean true as 1 and boolean false as 0 when it is used in mathematical expression. It converts empty string to a 0 when used in mathematical expression.
Number(true) // 1 Number(false) // 0 Number("") // 0 true + true // 2 4 + true // 5 3 * false // 0 3 * "" // 0 3 + "" // "3"
The valueOf method defined in the Object is used by Javascript whenever we pass an Object where it expects a string or numeric value. The valueOf method defined on an Object takes precedence (for both mathematical/string expressions) and is used even if the Object has defined the toString method. The Number object has a valueOf() method defined which returns a numeric value.
const foo = { valueOf: () => 3 } 3 + foo // 6 3 * foo // 9 const bar = { toString: () => 2, valueOf: () => 5 } "sa" + bar // "sa5" 3 * bar // 15 2 + bar // 7
Global Object
Historically the global object in the web browser is window or frames. The Web Workers API which has no browsing context, uses self as a global object. Node JS uses the global keyword to reference the global object. In order to standardize this and make it available across all environments, ES2020 introduced the globalThis object.
Execution Context
When a JavaScript engine executes a script, it creates global and function execution contexts. Each execution context has two phases: the creation phase and the execution phase. When a script executes for the first time, the JavaScript engine creates a Global Execution Context. During the creation phase of Global context, it performs the following tasks:
- Create a global object i.e., window in the web browser or global in Node.js.
- Create a this object binding which points to the global object above.
- Setup a memory heap for storing variables and function references.
- Store the function declarations in the memory heap and variables within the global execution context with the initial values as undefined.
During the execution phase, the JavaScript engine executes the code line by line. In this phase, it assigns values to variables and executes the function calls. For every function call, the JavaScript engine creates a new Function Execution Context. The Function Execution Context is similar to the Global Execution Context, but instead of creating the global object, it creates the arguments object that contains a reference to all the parameters passed into the function:
Javascript uses call stack to keep track of all the execution contexts including the Global Execution Context and Function Execution Contexts.
Functions
Javascript allows to pass less or more arguments while calling a function. If we pass less arguments then rest of the parameters will be undefined. If we pass more arguments then additional arguments will be ignored. Javascript has an arguments object which includes value of each parameter passed to the function. The values of the argument object can be accessed using index similar to an array. An arguments object is still valid even if function does not include any parameters.
function ShowMessage(firstName, lastName) { alert("Hello " + arguments[0] + " " + arguments[1]); } function ShowMessage() { console.log("Hello " + arguments[0] + " " + arguments[1]); } ShowMessage("Steve", "Jobs"); // displays Hello Steve Jobs
In JavaScript, a function can have one or more inner functions. These nested functions are in the scope of outer function. The inner function always has access to the variables and parameters of its outer function, even after the outer function has returned, known as Closure. However, outer function cannot access variables defined inside inner functions. A function can also return another function in JavaScript. A function can be assigned to a variable and then the variable can be uses as a function, called as function expression. JavaScript allows to define a function without any name called the anonymous function. Anonymous function must be assigned to a variable.
var showMessage = function (){ console.log("Hello World!"); }; showMessage();
All functions in JavaScript are objects. They are the instances of the Function type. Since functions are objects, they have properties and methods like other objects. Each function has two properties, the length property which determines the number of named arguments specified in the function declaration and the prototype property references the actual function object.
function swap(x, y) { let tmp = x; x = y; y = tmp; } console.log(swap.length); console.log(swap.prototype);
The function can be called as a constructor to create a new object, e.g. as new function_name(). ES6 has added a new property called target.new which allows to detect whether a function is called as a normal function (target.new is undefined) or as a constructor using the new operator (target.new is constructor fn). The apply() and call() methods as discussed below call a function with a given this value and arguments. The bind() method creates a new function instance whose this value is bound to the object that we provide. The below example changes the this value inside the start() method of the car object to the aircraft object and the bind() method returns a new function that is assigned to the taxiing variable.
let car = { speed: 5, start: function() { console.log('Start with ' + this.speed + ' km/h'); } }; let aircraft = { speed: 10, fly: function() { console.log('Flying'); } }; let taxiing = car.start.bind(aircraft);
This also can be achieved directly by using the call method as below.
car.start.call(aircraft);
Template Literals
A back tick is used for template literal, were '${..}' specifies an expression.
const price = 12.34; console.log("The price is $" + price); console.log(`The price is $${price}`);
Object Literals
Prior to ES6, an object literal is a collection of name-value pairs.
const name = 'Samuel'; const age = 23; const person = { name: name, age: age };
ES6 allows to eliminate the duplication when a property of an object is the same as the local variable name by including the name without a colon and value as below. Internally, when a property of an object literal only has a name, the JavaScript engine searches for a variable with the same name in the surrounding scope. If it can find one, it assigns the property the value of the variable.
const samuel = { name, age }; console.log(samuel);
Error Handling
Javascript uses try-catch-finally block for error handling. In order to throw a custom error, the throw keyword is used with the Error object. The Error object is a base object for user-defined exceptions. The Error object has name, message and other properties. JavaScript allows to use throw with any argument, so technically the custom error classes need not inherit from Error. Although it becomes possible to use object instanceof Error to identify the error objects.
try { someMethod(); } catch (e) { if (e instanceof RangeError) { console.log(e); throw "Range Error occurred"; } else { throw e; } } finally { console.log("Always execute finally"); }
Computed property name
The square brackets allows to use the string literals and variables as the property names.
let name = 'machine name'; let machine = { [name]: 'server', 'machine hours': 10000 }; console.log(machine[name]); // server
In ES6, the computed property name is a part of the object literal syntax, and it uses the square bracket notation. When a property name is placed inside the square brackets, the JavaScript engine evaluates it as a string. Hence we can use an expression as a property name as below.
let prefix = 'machine'; let machine = { [prefix + ' name']: 'server', [prefix + ' hours']: 10000 }; console.log(machine['machine name']); // server
Concise method syntax
Prior to ES6, when defining a method for an object literal, we specify the name and full function definition as shown below.
let server = { name: "Server", restart: function () { console.log("The" + this.name + " is restarting..."); } };
ES6 makes the syntax for making a method of the object literal more succinct by removing the colon (:) and the function keyword. It’s valid to have spaces in the property name. The method is called using object_name['property name']();
let server = { name: 'Server', restart() { console.log("The " + this.name + " is restarting..."); }, 'starting up'() { console.log("The " + this.name + " is starting up!"); } }; server['starting up']();
for...of loop (ES6)
The for...of loop iterates over iterable objects such as Array, String, Map, Set, arguments or NodeList and any object implementing the iterator protocol. Each iteration assigns a property of the iterable object to the variable which can be declared as var, let, or const. The for...of loop also iterates over characters of a string. The for...of iterates over elements of any collection that has the [Symbol.iterator] property.
let scores = [80, 90, 70]; for (let score of scores) { score = score + 5; console.log(score); }
In order to access the index of the array elements inside the for loop, the entries() method of array is used, which returns a pair of [index, element] for each iteration of the array.
let colors = ['Red', 'Green', 'Blue']; for(const entry of colors.entries()) { console.log(`${entry[0]}--${entry[1]}`); } for (const [index, color] of colors.entries()) { console.log(`${color} is at index ${index}`); }
for...in loop
The for...in iterates over all enumerable properties of an object but does not iterate over a collection such as Array, Map or Set.
var person = { firstName: 'John', lastName: 'Doe', ssn: '299-24-2351' }; for(var prop in person) { console.log(prop + ':' + person[prop]); }
for each loop
The forEach method is used to loop through arrays. It passes a callback function for each element of an array with parameters current value, optional index and optional array object.
const numbers = [1, 2, 3, 4, 5]; numbers.forEach(number => { console.log(number); }); numbers.forEach((number, index) => { console.log('Index: ' + index + ' Value: ' + number); }); numbers.forEach((number, index, array) => { console.log(array); });
Collections
The Set object stores unique values of any type, whether primitive values or object references. The elements of a set are stored in order of insertion.
let nums = new Set([1, 2, 3]);
The Map object holds key-value pairs and retains the original insertion order of the keys. Both primitive data types and Objects can be used as keys or values.Map
let colors = new Map(); colors.set('red', '#ff0000'); colors.set('green', '#00ff00'); colors.set('blue', '#0000ff');
Decorators, func.call and func.apply
A Decorator function is a higher order function that modifies the behavior of the function or method passed to it by returning a new function. Below is an example of decorator function.
function amount(x) { console.log(`Amount is ${x}`); return x; } function dollarDecorator(func) { return function(x) { let result = func(x + "$"); return result; }; } amount = dollarDecorator(amount); console.log( amount(45.67) );
The above decorator is not suitable for object methods as decorator function calling the original object method will fail as it does not have access to the object instance (this). This is resolved using Javascript func.call() function. The func.call(context, ...args) is a special built-in function method that allows to call a function explicitly setting this. The syntax is: func.call(context, arg1, arg2, ...)
The func.call executes func by providing the first argument as this, and the next as the arguments. The below two calls are exactly the same.
func(1, 2, 3); func.call(obj, 1, 2, 3)
The func.apply is same as func.call, only that the call expects a list of arguments, while apply takes an array-like object with them.
func.call(this, ...arguments) func.apply(this, arguments)
Generators
The yield keyword allows to pause and resume a generator function (function*) asynchronously. A generator function similar to a normal function but whenever it is returning any value, it does that using the ‘yield’ keyword instead of returning it directly. Yield can’t be called from nested functions or from callbacks.
The yield expression returns an object with two properties, the “value” which is the actual value and “done” which is a boolean value, it returns true when generator function is full completed else it returns false. If we pause the yield expression, the generator function will also get paused and resumes only when we call the next() method. When the next() method is encountered the function keeps on working until it faces another yield or returns expression.
The syntax of yield is [variable_name] = yield [expression];, were the expression specifies the value to return from a generator function via the iteration protocol, and variable_name stores the optional value passed to the next() method of the iterator object.
function* showValues(index) { while (index < 3) { yield index; index++; } } const iterator = showValues(1); console.log(iterator.next().value); // output: 1 console.log(iterator.next().value); // output: 2 for(let value of showValues(1)) { console.log(value); // 1, then 2 }
Arrow Functions
Arrow function is an anonymous function which is similar to lambda expressions in other languages. An arrow function expression is a compact alternative to a traditional function expression, but is limited and can't be used in all situations. Arrow function is represented as (parameter list) => body. Arrow functions support rest and default parameters, and de-structuring within parameters.
// original function const square = function(number) { return number * number; } // arrow function const square = (number) => { return number * number; }
Arrow function with a single parameter
const square = number => { return number * number; }
If body of function contains single line and returns a value then we can write as
const square = number => number * number;
Scoping defines the range of functionality of a variable so that it may only be called (referenced) from within the block of code in which it is defined. There are two main types of scoping in programming languages.
- Lexical Scoping: An unbounded variable is bounded to a definition in the defining scope.
- Dynamic Scoping: An unbounded variable is bounded to a variable passed in by the caller of the function.
In regular traditional function, all variables are lexically scoped except this and arguments which are dynamically scoped. In an arrow function, all variables (including this and arguments) are lexically scoped. Hence arrow functions should not be used as a method of a class, due to lexical binding of this. The arrow function does not have its own bindings to this and super. It does not have arguments, or new.target keywords. It is not suitable for call, apply and bind methods, which generally rely on establishing a scope. It cannot use yield, within its body.
const somedata = 34; this.newdata = 12; const example = function(n) { console.log(n); console.log(stuff); // lexically scoped console.log(this.newdata); // dynamic for regular function, lexical for arrow function } example(10); example.call({ newdata: 23 }, 10);
When a normal function is called as a standalone function outside an object by default the this keyword returns the window object. When we change the function() to an arrow function then it will not reset 'this' value, instead it inherits the 'this' keyword in the context in which the code is defined.
const stdfunc = { show() { setTimeout(function() { // this returns reference to window object. console.log("this", this); }, 1000); } } const arrowfunc = { show() { setTimeout(() => { // this returns reference to window object. console.log("this", this); }, 1000); } } stdfunc.show(); arrowfunc.show();
Filter function
The filter() method creates an array filled with all array elements that passes the test provided by argument function.
const jobs = [ { id: 1, isActive: true }, { id: 2, isActive: true }, { id: 3, isActive: false } ]; // filter method takes the function as a predicate to decide wether to keep or filter out each element of array. const activeJobs = jobs.filter(function(job) { return job.isActive; } ) const activeJobs = jobs.filter(job => job.isActive)
Map function
The Array.map() function is used to transform a list of items.
const colors = ['red', 'green', 'blue']; // the map() function transforms each element in the array to a html <li/> list element const items = colors.map(function(color) { return '<li>' + color + '</li>'; }); const items = colors.map(color => '<li>' + color + '</li>'); // use template literals, we define template for string const items = colors.map(color => `<li>${color}</li>`); console.log(items);
The map can be used to convert the array of one type of object into array of another object type. It can also convert array of objects to primitive arrays with values from object properties.
const records = [ {name: 'Week 1', owner: 0, others: 4, amt: 4}, {name: 'Week 2', owner: 0, others: 7, amt: 7} ]; var result = records.map(record => ({ value: record.amt, text: record.name })); let arrayOwner = []; // end result [0, 0, 0, 0, 0, 0, 0] let arrayOthers = []; // end result [2, 4, 3, 6, 3, 8, 9] const res = records.map((e,i) => { e.owner = arrayOwner[i]; e.others = arrayOthers[i]; return e; });
Curry function
A curried function takes multiple arguments one at a time, were for each curried argument, it returns a function that takes the next argument, with the last function returning the result of applying the function to all of its arguments.
// Normal function const sum = (x, y, z) => x + y + z; console.log(sum(1, 5, 6)); // Curried function var sum = function sum(x) { return function (y) { return function (z) { return x + y + z; }; }; }; console.log(sum(1)(5)(6));
We have three nested functions each one is taking one argument and returning the following function that takes the next argument, except that last function which is doing the calculation.
const sum = x => y => z => x + y + z; console.log(sum(1)(5)(6));
DeStructuring
The object destructuring is a useful JavaScript feature to extract properties from objects and bind them to variables. Object destructuring can extract multiple properties in single statement, access properties from nested objects, and can set a default value if the property doesn't exist.
const address = { street: 'Downing Street', city: 'London', country: 'UK' }; const street = address.street; const city = address.city; const country = address.country; const { street, city, country } = address; const { street } = address; const { street: st } = address;
Object destructuring also works on the returned object from the function by extracting the required object attributes into variables.
const getPerson = function() { return {extracting first: 'John', first: 'Fitzerald', first: 'Kennedy' } }; // Here first is the object property, while firstName is the corresponding local variable const {first: firstName, last: lastName} = getPerson(); console.log(`${firstName} ${lastName}`); const {first, last} = getPerson(); console.log(`${first} ${last}`);
Below is example of object destructuring in the function parameter.
const john = { name: 'John F Kennedy', age: 37, address: { street: '1600 Pennsylvania Avenue' }, mailing: { street: 'Boston' } } const printName = function({ name, age, address: {street}, mailing: { street: mailstreet } }) { console.log(`${name} is ${age} years old`); console.log(`${name} address street is ${street} and mailing street is ${mailstreet}`); } printName(john);
Destructing can also be used in for...of loop, defining the variable which extracts property from the object.
const ratings = [ {user: 'John',score: 3}, {user: 'Jane',score: 4}, {user: 'David',score: 5}, {user: 'Peter',score: 2}, ]; let sum = 0; for (const {score} of ratings) { sum += score; } console.log(`Total scores: ${sum}`);
Destructuring using array, were individual values of the array is extracted into variables.
const getPerson = function() { return ['John', 'Fitzgerald', 'Kennedy']; } const [first, , last] = getPerson(); console.log(`${first} ${last}`); const [firstname, ...allelse] = getPerson(); console.log(`${firstname} ${allelse}`);
Spread Operator
The spread operator (...) allows to spread out elements of an iterable object such as an array, map, or a set. It unpacks the elements of the array, map or list. The spread operator comes handy to construct an array using literal form, concatenating/combining arrays or copying an array.
const first = [1, 2, 3]; const second = [4, 5, 6]; const combined = first.concat(second); // spread all items in first array (i.e. get all items in first array) and then spread second array together to form new array const combined = [...first, ...second] const combined = [...first, 'a', ...second, 'b'] // clone array const clone = [...first];
Spread operator can also be used create new object from existing objects.
const first = { name: 'Jarvis' }; const second = { job: 'Instructor'}; const combined = {...first, ...second, location: 'USA'}; const clone = {...first}; const user = Object.freeze({ name: 'James', age: 23 }); const updatedUser = { ...james, age: james.age + 1 };
Rest Parameter
The rest parameter (...) collects all remaining arguments of a function into an array. The rest parameters must be the last arguments of a function. The rest parameter packs elements into an array, while the spread operator unpacks elements.
const max = function(...numbers) { numbers.reduce((large, e) => e > large ? e : large); } const values = [1, 12, 7]; console.log(max(values[]));
Default Parameter
The default function parameters allows to initialize the named parameters with default values if no values or undefined are passed into the function. They enable the existing functions to extend by adding new parameters. Default parameters can also use the values passed to the function to its left. When null is passed as a value for the default parameter, then its value becomes null. On the other hand if undefined is passed as the default parameter, then it is replaced by the default value if the default parameter is specified.
const greet = function(name, msg = `Hi${name.length}`) { console.log(`${msg} ${name}`); } greet('Jerry');
Object Properties
In JavaScript, an object is an unordered list of key-value pairs. The key is usually a string or a symbol. The value can be a value of any primitive type (string, boolean, number, undefined, or null), an object, or a function. The following example creates a new object using the object literal syntax:
const person = { firstName: 'John', lastName: 'Doe };
JavaScript specifies characteristics of properties of objects via internal attributes surrounded by the two pair of square brackets. There are two types of object properties: data properties and accessor properties.
A data property contains a single location for a data value. A data property has four attributes:
- [[Configurarable]] – determines whether a property can be redefined or removed via delete operator.
- [[Enumerable]] – indicates that if a property will be returned in the for...in loop.
- [[Writable]] – specifies that the value of a property can be changed.
- [[Value]] – contains the actual value of a property.
By default, the [[Configurable]] , [[Enumerable]], and [[Writable]] attributes set to true for all properties defined directly on an object. The default value of the [[Value]] attribute is undefined.
The Enumerable attribute determines whether or not a property is accessible when the object’s properties are enumerated using the for...in loop or Object.keys() method. By default, all properties created via a simple assignment or via a property initializer are enumerable.
Object attributes of a property can be changed using Object.defineProperty() method, which takes the object, property name and the property descriptor for four properties (configurable, enumerable, writable, and value). The below example creates a person object and adds the ssn property to it using the Object.defineProperty() method.
let person = {}; Object.defineProperty(person, 'ssn', { configurable: false, value: '012-38-9119' }); delete person.ssn;
Accessor properties, similar to data properties also have [[Configurable]] and [[Enumerable]] attributes. They have the [[Get]] and [[Set]] attributes instead of [[Value]] and [[Writable]].
When you read data from an accessor property, the [[Get]] function is called automatically to return a value, which is undefined by default. When a value is assigned to an accessor property, the [[Set]] function is called automatically. An accessor property can be defined using the Object.defineProperty() method as below.
let person = { firstName: 'John', lastName: 'Doe' } Object.defineProperty(person, 'fullName', { get: function () { return this.firstName + ' ' + this.lastName; }, set: function (value) { let parts = value.split(' '); if (parts.length == 2) { this.firstName = parts[0]; this.lastName = parts[1]; } else { throw 'Invalid name format'; } } });
The Object.getOwnPropertyDescriptor() method allows to get the descriptor object of a property. It returns a descriptor object that describes four properties; configurable, enumerable, writable, and value.
Classes
Modern Javascript has introduced the class construct which provides great features of object oriented programming. The object of the class is initialized by the new keyword which automatically calls the constructor(). The value if this inside the constructor equals to the newly created instance. If a constructor is not defined for the class, a default constructor is created. The default constructor is an empty function, which doesn’t modify the instance.
class Person { region = "Europe"; constructor(name) { this.name = name; } walk() { console.log(this.name + " walks"); } } const person = new Person('Randy'); person.walk();
Javascript class and constructor is a syntactic sugar which uses functions and prototype to provide the class like entity. Internally javascript creates a function with the class name, that becomes the result of the class declaration. The function code is taken from the constructor method. The class methods are set as prototype for the function. The class field syntax allows to add properties to the class.
// create constructor function function Person(name) { this.name = name; } // add method to prototype Person.prototype.walk = function() { console.log(this.name + " walks"); }; Person.prototype.region = "Europe"; let person = new Person("John"); person.walk();
Classes in Javascript can be used as an expression by assigning to a variable.
const example = class Car {}
Just like functions, classes can be defined inside another expression, passed around, returned, assigned, etc. Class can have a computed method name using brackets [...].
class User { ['say' + 'Hi']() { console.log("Hello"); } } new User().sayHi();
Javascript provides 2 levels if field accessibility, public field (by default) and private fields. A field can be made private by adding the prefix # to its name. The getter and setter mimic regular field, but allows more control on how the field is accessed and changed. The getter is executed on an attempt to get the field value, while setter on an attempt to set a value.
class User { #nameValue; constructor(name) { this.name = name; } get name() { return this.#nameValue; } set name(name) { if (name === '') { throw new Error(`name field of User cannot be empty`); } this.#nameValue = name; } } const user = new User('Jon Snow'); console.log(user.name); // The getter is invoked, returns 'Jon Snow' user.name = 'Jon White'; // The setter is invoked user.name = ''; // The setter throws an Error
Every class in javascript is internally a function, which cannot be invoked. The class constructor cannot be called directly without the new keyword.
class Car {} console.log(typeof(Car)); // returns function Car(); // TypeError: Class constructor Car cannot be invoked.
The new.target is a special value, which is available when a function is invoked as a constructor, in other invocations it is undefined.
const Car = function() { console.log(new.target); } new Car(); // prints [Function: Car] Car(); // prints undefined
Internally the new keyword performs following operations:
- It creates new empty object e.g. obj = { };
- It sets new empty object's invisible prototype property to be the constructor function's visible and accessible prototype property. Every function has visible prototype property whereas every object includes invisible prototype property.
- It binds property or function which is declared with this keyword to the new object.
- It returns newly created object unless the constructor function returns a non-primitive value such as a custom JavaScript object. If constructor function does not include return statement then compiler will insert 'return this;' implicitly at the end of the function. If the constructor function returns a primitive value then it will be ignored.
Fields are defined in the constructor of the class, not at the top of the class as in Java.
class Car { constructor(year) { this.year = year; this.mileage = 0; this.modelName = 'None'; } drive(distance) { this.mileage += distance; } get model() { return this.modelName; } get model(value) { return this.modelName = value; } } const car = new Car(2021); console.log(car); car.drive(100); console.log(car);
Javascript allows to add properties to a class by defining a method as a getter with "get" keyword. The getter method should not take any arguments. We cannot change the property when the class only has a getter for the property.
console.log(car.model); car.model = 'Tesla R100' console.log(car.model);
Classes can be dynamically created in javascript as shown in the below example.
const classFactory = function(...properties) { return class { constructor(...values) { for(const [index, property] of properties.entries()) { this[property] = values[index]; } } }; } const Book = classFactory('title', 'pages'); const book1 = new Book('Lord of the Rings', 9800); console.log(book1);
Static Methods
The static methods are directly attached to the class and not with any instances of the class. Before ES6, static methods we defined by adding the method directly to the constructor. In ES6, static keyword is used to define the static methods. The static methods are called via the class name, not the instances of the class.
class Car { constructor(name) { this.name = name; } static createCar(fuel) { let name = fuel == "electric" ? "Tesla RX100" : "Toyota Corolla"; return new Car(name); } static info() { console.log('This is Car info'); } static get model() { return 'Tesla RX100'; } } Car.info(); console.log(Car.model); let anonymous = Car.createCar("electric");
Similar to instance field/method, static field and methods can be made private by adding the special symbol # as prefix.
class User { static #instances = 0; name; constructor(name) { User.#instances++; if (User.#instances > 2) { throw new Error('Unable to create User instance'); } this.name = name; } } const user1 = new User('user1');
Static fields can be defined outside the class as below.
class Car { constructor() { Car.rating++; } } Car.rating = 10; new Car(); console.log(Car.rating);
In order to call a static method from a class constructor or an instance method, the class name is used followed by the . and the name of the static method, i.e. className.staticMethodName(). Alternatively we could also use the this.constructor.staticMethodName() notation.
this Keyword
The this keyword refers to the object instance of the class and allows to access the class instance fields and methods. The this keyword can be used in both global and function contexts, but its behavior changes between strict and non-strict mode. The this keyword references the object that is currently being called by the function. In the global context, the this references the global object, which is the window object on the web browser or global object on Node JS.
When a function is invoked in non-strict mode, then this keyword references the global object, which is the window on the web browser and global on Node JS. But in the strict mode, the this keyword in the function is set to undefined.
When a method of an object is invoked using the object dot notation then the this keyword is set to the object that owns the method. But when the method is called directly (e.g. using function variable) without specifying its object then this keyword is set to the global object in non-strict mode and undefined in the strict mode. To fix this issue, the bind() method of the Function.prototype object. The bind() method creates a new function whose the this keyword is set to a specified value. The below bind method returns a new instance of the walk function were the this reference points to person object passed to the bind() function.
const person = { name: 'Jarvis', walk() { console.log(this); } } person.walk(); const walk = person.walk; console.log(walk); const walk = person.walk.bind(person); walk();
When the new keyword is used to create an instance of a function object, the function is used as a constructor. The function as a constructor creates a new object and sets this to the newly created object. To ensure that the function is always invoked using new keyword constructor invocation, the meta-property named new.target is used which detects whether a function is invoked as a simple invocation or as a constructor.
function Animal(type) { if (!new.target) { throw new Error("Animal() must be called with new."); } this.type = type; } const herbivore = new Animal("herbivore"); console.log(animal.type); // Throws error as constructor function is invoke without new keyword const carnivore = Animal("carnivore");
Inheritance
Traditional languages such as Java, CSharp have Class based inheritance were one class inherits from another class which is statically defined in the code, called classical inheritance. Javascript on the other hand uses Prototypal inheritance which is dynamic in nature. In prototypal inheritance, an object inherits properties from another object via the prototype linkage. It allows to change the behavior of the object on the fly without changing its class.
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. 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 the constructor function.
function Student() { this.name = 'John'; this.gender = 'M'; } Student.prototype.age = 15; var studObj1 = new Student(); console.log(studObj1.age); // 15 var studObj2 = new Student(); console.log(studObj2.age); // 15 Student.prototype = { age : 20 }; var studObj3 = new Student(); console.log(studObj3.age); // 20 console.log(Student.prototype); // object console.log(studObj1.prototype); // undefined console.log(studObj1.__proto__); // object console.log(typeof Student.prototype); // object console.log(typeof studObj1.__proto__); // object
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. The Object.getPrototypeOf(obj) method is used instead of __proto__ to access prototype object of an object. Each object's prototype is linked to function's prototype object. If we change function's prototype then only new objects will be linked to changed prototype. All other existing objects will still link to old prototype of function.
function Student() { this.name = 'John'; this.gender = 'M'; } var studObj = new Student(); Student.prototype.sayHi = function(){ console.log("Hi"); }; var studObj1 = new Student(); var proto = Object.getPrototypeOf(studObj1); // returns Student's prototype object console.log(proto.constructor); // returns Student function
The prototype object is used to implement inheritance in Javascript. In the below example, we set the Student.prototype to newly created person object. The new keyword creates an object of Person class and also assigns Person.prototype to new object's prototype object and then finally assigns newly created object to Student.prototype object. Optionally, we can also assign Person.prototype to Student.prototype object.
function Person(firstName, lastName) { this.FirstName = firstName; this.LastName = lastName; }; Person.prototype.getFullName = function () { return this.FirstName + " " + this.LastName; } function Student(firstName, lastName, schoolName, grade) { Person.call(this, firstName, lastName); this.SchoolName = schoolName || "unknown"; this.Grade = grade || 0; } //Student.prototype = Person.prototype; Student.prototype = new Person(); Student.prototype.constructor = Student; var std = new Student("James","Bond", "XYZ", 10); console.log(std.getFullName()); // James Bond console.log(std instanceof Student); // true console.log(std instanceof Person); // true
An object has a prototype object, which in turn can have its own prototype object, which could be chained were each prototype refers to each other until the last prototype which does not have any prototype set. When a method is called on such object, it checks for the corresponding method through the prototype chain across all prototypes until it either finds a match or it reaches end of prototype chain with no match.
const use = function(person) { try { person.work(); } catch(ex) { console.log('Not Found'); } } const john = { name: 'John Corner' }; const employment = { work : function() { console.log('Working...'); } } Object.setPrototypeOf(john, employment); use(john);
As mentioned above, prototype can also be set to the class instead of an object. The gets in javascript are deep while sets are shallow.
const Car = function() { this.drive = function(distance) { this.mileage += distance; } } Car.prototype.mileage = 0; const car1 = new Car(); console.log(Object.getPrototypeOf(car1)) console.log(car1.mileage); const car2 = new Car(); car1.drive(100); console.log(car1.mileage); console.log(car2.mileage);
Javascript refined the syntax providing extends keyword, but under the hood it works as above. The constructor is optional for child class, were if a constructor is not provided, Javascript internally generates a default constructor for the child class. Below is the example using modern syntax which inherits all the methods (including constructor) defined in parent class.
class Person { constructor(firstName, lastName) { this.firstName = firstName; this.firstName = firstName; } toString() { return `${this.firstName} ${this.lastName}`; } } class Employee extends Person { constructor(firstName, lastName, jobTitle) { super(firstName, lastName); this.jobTitle = jobTitle; } toString() { return `${super.toString} ${this.jobTitle}`; } } const james = new Employee('James', 'Bond', 'Spy'); console.log(james.toString()); console.log(Object.getPrototypeOf(james));
Javascript allows built-in classes like Array, Map etc to also be extendable. The special keyword super is introduced to call the parent constructor from the child class, and to access the parent fields and methods from the child class. The instanceof operator with syntax object instanceof Class, determines if the object is an instance of Class, taking inheritance into account.
Symbol
Javascript does not have the concept of Interfaces like Java. Symbol is a new primitive type in JavaScript intended for limited specialized use.
- To define properties for objects in such a way they don’t appear during normal iteration—these properties are not private; they’re just not easily discovered like other properties.
- To easily define a global registry or dictionary of objects.
- To define some special well-known methods in objects; this feature, which fills the void of interfaces, is arguably one of the most important purposes of Symbol.
const s1 = Symbol.for('hi'); const s2 = Symbol.for('hi'); console.log(s1 === s2); // Returns True
Symbol can become a method name which gives us a unique method name to avoid method name collision.
class Person { [Symbol.for('play')]() { console.log('playing...'); } } const john = new Person(); const playMethod = Symbol.for('play'); john[playMethod]();
Iterators
Javascript has two types of iteration protocols, iterable protocol and iterator protocol.
An object is an iterator when it has a next() method which returns an object with the current element and whether any more elements that could be iterated upon.
An object is an iterable when it contains a method called [Symbol.iterator] that takes no argument and returns an object which conforms to the iterator protocol. Javascript has predefined Symbol such as Symbol.iterator, Symbol.match which allows to add these methods to the existing classes.
class Records { constructor() { this.names = 'Ted', 'Jim', 'Tim']; } [Symbol.iterator]() { let index = 0; const self = this; return { next: function() { return { done: index == 4, value: self.names[index++] }; } }; } }
Example of a Generator using Symbol.
lass Records { constructor() { this.names = 'Ted', 'Jim', 'Tim']; } *[Symbol.iterator]() { for(const name of this.names) { yield name; } } yield *this.names; }
Proxies
A Proxy is an object that wraps another object (target) and intercepts the fundamental operations of the target object. The fundamental operations can be the property lookup, assignment, enumeration, and function invocations, etc. The Proxy object takes the target object to wrap and the handler object containing methods to control the behaviors of the target. The methods inside the handler object are called traps.
const user = { firstName: 'John', lastName: 'Dillenger' } const handler = { get(target, property) { return property === 'fullName' ? `${target.firstName} ${target.lastName}` : target[property]; } }; const proxyUser = new Proxy(user, handler); console.log(proxyUser.fullName);
Modules
Modules allow to structure the project by moving each class in a separate file.
// File: person.js class Person {} // File: teacher.js class Teacher {}
Objects defined in module are private by default and won’t be added automatically to the global scope. The export statement (were export keyword is placed in front) exposes the specified variable, function, or a class from the current file into other modules. The export keyword requires the name of the function or class to be exported, called named exports, hence anonymous function or class cannot be exported. One or more objects can be exported from a given module using names. Variables can be defined first and exported in later statements.
export class Person {} export class Teacher extends Person {}
The import statement imports the exported variables, functions or class from the original module file. In order to import multiple bindings, they are explicitly listed inside the curly braces. After the first import statement, the specified module is executed and loaded into the memory, and it is reused whenever it is referenced by the subsequent import statement.
import { Person } from './person';
JavaScript allows you to create aliases for variables, functions, or classes when you export and import.
export { add as sum }; import {sum as total} from './math.js';
It is possible to export bindings that have been imported, called as re-exporting.
export {sum} from './math.js';
Everything can be imported as a single object from a module, using the asterisk (*) pattern as below, which is called as namespace import.
import * as cal from './cal.js';
Default export is were a single (main) object is exported from module. A module can have one and only one default export. The default for a module can be a variable, a function, or a class. The default export is easier to import as we only need to specify the name for the function because the module represents the function name. Below example exports the teacher class as default export from the module.
export default class Teacher extends Person { ... }
While importing the default object from the module, we don't need curly braces, as default object can be imported directly from the module.
import Teacher from './teacher';
Both default and non-default bindings (exports) can be imported together, were we specify a list of bindings after the import keyword were the default binding come first and non-default binding is surrounded by curly braces..
// sort.js export default function(arr) { .. } export function heapSort(arr) { .. } import sort, {heapSort} from './sort.js'; import Teacher, { promote } from './teacher';
Asynchronous functions
Javascript maintains a call stack, were it adds the function which is being invoked and pops it out once the function execution returns. The call stack has the main() function which is the default function when the javascript is executed.
When a function simply accepts another function as an argument, this contained function is known as a callback function. The callback function is not run unless called by its containing function, it is called back. Callback functions can be named or anonymous functions. Multiple functions can be created independently and used as callback functions, creating multi-level functions. When such function tree created becomes too large, the code becomes incomprehensible, difficult to refactor and known as callback hell.
Javascript provides setTimeout(), setInterval() and requestAnimationFrame() as asynchronous functions which takes a callback function and allow them to execute asynchronously after a certain time interval. The setTimeout allows to delay the execution of function, until after the delay time interval (in milliseconds). The setInterval allows us to run a function repeatedly, starting after the interval of time, then repeating continuously at that interval. The callback function is executed asynchronously after the specified delay time period. The asynchronous functions are sent to Web API (instead of callstack), which keeps track of the timer and once the time is expired it adds the function back to the callstack.
function login(email, password, callback) { setTimeout(() => { callback({ userEmail: email }); }, 5000); } function getUserDetails(email, callback) { setTimeout(() => { callback({ age: 12, address: 'California' }); }, 3000); } const user = login('test@email.com', 12345, user => { console.log(user.userEmail); getUserDetails(user.userEmail, details => { console.log(details.address); }); });
Promises
In order to avoid the nested structure of callback were output of one function is dependent to call the next function, Promise were introduced. A promise is defined as a proxy for a value that will eventually become available. A promise is an object that gives either the result or a failure for an asynchronous operation.
The Promise constructor accepts a function as an argument which is called the executor. The executor accepts two functions named resolve() and reject(). When we call new Promise(executor), the executor is called automatically. Inside the executor, the resolve() function is called manually if the executor is completed successfully, or else the reject() function is invoked in case of an error. Once a promise has been called, it will start in a pending state. The calling function continues its execution until the promise is in pending state. Once the promise goes into resolved or reject state the corresponding callback function is called. The then() method is used to schedule a callback to be executed when the promise is successfully resolved. The catch() method used to schedule a callback to be executed when the promise is rejected, and the finally() method which executes the callback whether the promise is fulfilled or rejected.
const promise = new Promise((resolve, reject) => { setTimeout(() => { resolve({ user: 'Ed' }); }, 2000); // For error case use reject() // setTimeout(() => { // reject(new Error('User access denied.')); // }, 2000); }); promise.then(user => { console.log(user); }) .catch(err => console.log(err.message));
A promise can be returned to another promise, creating a chain of promises.
function login(email, password) { return new Promise((resolve, reject) => { setTimeout(() => { resolve({ userEmail: email }); }, 5000); }); } function getUserDetails(email) { return new Promise((resolve, reject) => { setTimeout(() => { resolve({ age: 12, address: 'California' }); }, 3000); }); } login('test@email.com', 12345) .then(user => getUserDetails(user.userEmail)) .then(detail => console.log(details.address));
The Promise.all() allows to synchronize different promises (in parallel) by defining a list of promises, and execute until all are resolved. The Promise.all() accepts a list of Promises and returns a Promise that either resolves when every input Promise resolves or rejected when any of the input Promise is rejected.
const service1 = new Promise(resolve => { setTimeout(() => { resolve({ values: [1, 2, 3, 4] }); }, 2000); }); const service2 = new Promise(resolve => { setTimeout(() => { resolve({ quote: 'Wholesale' }); }, 3000); }); Promise.all([service1, service2]) .then(result => console.log(result));
The Promise.race() returns the promise instance which is firstly resolved or rejected. The race method returns a promise that fulfills or rejects as soon as there is one promise that fulfills or rejects, with the value or reason from that promise. It is used to run the attached callback only when the first promise is resolved. The Promise.all() returns a promise that resolves to an array of values from the input promises while the Promise.race() returns a promise that resolves to the value from the first settled promise.
Fetch API
The Fetch API is a promise-based API for making asynchronous HTTP requests in the browser similar to XMLHttpRequest (XHR). Unlike XHR, it is a simple and clean API that uses promises to provides a more powerful and flexible feature set to fetch resources from the server.
The fetch() method is used to send a request to a URL which is passed as a required parameter. It returns a promise that passes the response to then() when it is fulfilled. The catch() method intercepts errors if the request fails to complete for any reason. The fetch response has status, statusText properties and a json() method, which returns a promise that will resolve with the content of the body processed and transformed into JSON. In order to post a request and additional options parameter is passed which contains method, body and headers object as attributes.
const user = { first_name: 'John', last_name: 'Lilly', job_title: 'Software Engineer' }; const options = { method: 'POST', body: JSON.stringify(user), headers: { 'Content-Type': 'application/json' } } fetch('https://server.com/api/users', options) .then(res => res.json()) .then(res => console.log(res)); .catch(error => { console.log('Request failed', error) })
Async and Await
Async functions are a combination of promises and generators, and basically, they are a higher level abstraction over promises. In other words we can say that async/await are built on top of Promises.
async function displayUser() { try { const user = await login('test@email.com', 12345); const details = await getUserDetails(user.userEmail); console.log(detail); } catch(err) { console.log(err); } }
Prepending the async keyword to any function means that the function will return a promise. An async function always returns a promise. The keyword await before a function makes the function wait for a promise. The await keyword can only be used inside an async function.
const doSomethingAsync = () => { return new Promise(resolve => { setTimeout(() => resolve('I did something'), 3000) }) } const doSomething = async () => { console.log(await doSomethingAsync()) }
Async functions can be chained very easily, and the syntax is much more readable than with plain promises, as shown in the below example.
const getFirstUserData = async () => { const response = await fetch('/users.json') // get users list const users = await response.json() // parse JSON const user = users[0] // pick first user const userResponse = await fetch(`/users/${user.name}`) // get user data const userData = await userResponse.json() // parse JSON return userData } getFirstUserData();
The above example was represented as below using Promises which represents more chaining and arrow functions.
const getFirstUserData = () => { return fetch('/users.json') // get users list .then(response => response.json()) // parse JSON .then(users => users[0]) // pick first user .then(user => fetch(`/users/${user.name}`)) // get user data .then(userResponse => userResponse.json()) // parse JSON } getFirstUserData();
A regular function can call the async function as below.
async function wait() { await new Promise(resolve => setTimeout(resolve, 1000)); return 10; } function myfunction() { // shows 10 after 1 second wait().then(result => console.log(result)); } myfunction();
Node JS
Node JS provides an open-source, cross-platform runtime environment to execute Javascript code outside of the browser context. The Node JS application runs in a single process, without creating a new thread for every request. Node JS provides a set of asynchronous I/O primitives in its standard library that prevents JavaScript code from being blocked.
Contrary to the traditional HttpServers which allocates a single thread for each request and waits until for e.g. the resource is loaded from the database or external service. Node uses non-blocking or asynchronous model were a single thread is used to serve multiple requests. Node uses event queue were it monitors for received event messages and processes the events using the single thread. Hence this makes Node runtime environment ideal for high I/O intensive applications, were it can serve more client requests without requiring additional thread allocation. Node on the other hand should not be used for CPU-intensive applications which involves high amount of data processing. Also single threaded Node applications only utilizes a single CPU core, rendering other CPU cores unused. Although since Node JS v10.5.0, the new worker_threads module enables to use threads to execute JavaScript in parallel.
Node JS internally maintains a limited Thread Pool which has pre-allocated set of threads to service client requests. The UV_THREADPOOL_SIZE environment variable sets the number of threads used in libuv's threadpool to size threads.
process.env.UV_THREADPOOL_SIZE = 6;
When Node JS receives client requests, it places them into the event queue. Node JS internally has a Component called the “Event Loop” which uses an indefinite loop for polling the event queue for received events and processes them. If the client request does not require any blocking I/O operations, then everything is processed, response prepared and sent back to the client. On the other hand, if the client request requires some blocking I/O operations such as calling database, external services etc, then the event loop would check for thread availability in the internal Thread Pool, and assign the client request to a selected thread from the Thread pool. This Thread is responsible for taking that request, processing it, perform Blocking I/O operations, prepare response and send it back to the Event Loop. The Event Loop in turn would send back the corresponding response to the respective Client. The event loop allows Node JS to perform non-blocking I/O operations, even though JavaScript is single-threaded by offloading operations to the system Kernel whenever possible. Event loop model is provided by the Libuv library in Node JS.
Node JS can be installed by directly downloading Node JS binary for Windows and Mac OS. Mac users can also install using HomeBrew with brew install node. Node JS and npm can be installed in Ubuntu (and other linux flavors) using apt package manager as below. Alternatively node version manager can be used to install multiple versions of node and switch between them.
$ sudo apt update
$ sudo apt install nodejs
$ sudo apt install npm
Below command execute the javascript file using Node JS
$ node app.js
Node JS uses Google's V8 JavaScript engine for language implementation, hence new language features are first implemented in V8, and then incorporate in Node JS. To determine which features are supported for the Node JS release, we refer to the kangax.github.io, a dynamically generated feature list for all major JavaScript engines. For Node specific list, node.green is used, which leverages from the same data as kangax. Also we can print out the V8 version included in Node version using below command. The process.versions property returns an object listing the version strings of Node.js and its dependencies
$ node -p process.versions.v8
We can also list all the in progress features available on each Node JS release by grepping through the --v8-options argument.
$ node --v8-options | grep "in progress"
Node Modules
Node.js has a set of built-in modules which can use without any further installation. Node defines global object which provides various object/methods such as console.log(), setTimeout(), clearTimeout(), setInterval() etc. Node JS provides many modules, such as process, http, fs, events etc. which are discussed below. Node JS has built-in stream module providing the foundation upon which all streaming APIs are built.
Require and Export
The require(id) function is used to import node modules, JSON, and local files. Modules (both built-in and third party) can be imported from node_modules. Local modules and JSON files can be imported using a relative path e.g. (../dir/module).
// Importing a JSON file const jsonData = require('./path/filename.json'); // Importing a module from node_modules or Node.js built-in module const crypto = require('crypto');
The exports keyword is used to make properties and methods available outside the module file. In the below example we create custom module in user.js and export it to be used outside.
const getName = () => { return 'James Bond'; }; exports.getName = getName;
Now the above user.js module can be imported using require function as below.
const user = require('./user'); console.log(`User: ${user.getName()}`);
Process Module
The process core module of Node JS provides the env property which hosts all the environment variables that were set at the moment the process was started. The environment variables can be passed as below while executing a Node JS application.
$ USER_ID=239482 REGION=us-east-1 node server.js
The environment variables can be access using the below code.
process.env.USER_ID // "239482"
If there are multiple environment variables in the node project, we can create an .env file in the root directory of the project, and then use the dotenv package to load them during runtime as shown in the below example.
require('dotenv').config(); process.env.USER_ID // "239482"
The process module also provides the process.nextTick() function. When one iteration is completed of the event loop, it is known as a tick. The process.nextTick() is a callback function which is executed after completing the current iteration/tick of the event loop. It adds to the nextTickQueue which processes all the callbacks after completing current iteration and before starting the next iteration of event loop. The process.nextTick() is used for resource cleanup, handle errors or to run a request before next iteration.
const process = require('process') process.nextTick(() => { //do something })
In order to execute a piece of code asynchronously, the setImmediate() function of Node JS is used.
setImmediate(() => { //run something })
A function passed to process.nextTick() is going to be executed on the current iteration of the event loop, before the setTimeout(() => {}, 0) and setImmediate.
HTTP Module
Node JS provides http module which has libraries for networking such as for setting up services or providing client to invoke services. The below example creates new HTTP server using the createServer() method.
const http = require('http'); const hostname = '127.0.0.1' const port = process.env.PORT const server = http.createServer((req, res) => { res.writeHead(200, {'Content-Type': 'text/plain'}); res.write('Hello World!'); res.end(); }) server.listen(port, hostname, () => { console.log(`Server running at http://${hostname}:${port}/`) })
The http module also can be used to call http services as shown in below example.
const https = require('https') const payload = JSON.stringify({ "id": 1, "name": "Tim Scott" }); const options = { hostname: 'emprovise.com', port: 443, path: '/api/user', method: 'POST', headers: { 'Content-Type': 'application/json', 'Content-Length': payload.length }, timeout: 5000 } const req = https.request(options, res => { if(res.statusCode > 299) { let error = new Error(res.statusMessage); error.code = res.statusCode; reject(error); } res.on('data', d => { data += d; }); res.on('end', () => { resolve(data); }); }); req.on('error', error => { reject(error); }); req.on('timeout', () => { req.abort(); }); req.write(payload); req.end()
The net module allows to create custom socket clients and connect with the server. The net.connect() method allows to directly connect with the server without creating sockets.
let net = require('net'); let client = new net.Socket(); client.connect(8080, '127.0.0.1', function() { console.log('Connected'); client.write('Hello from Client'); }); client.setEncoding('utf8'); client.on('data', function(data) { console.log('Received: ' + data); client.destroy(); }); client.on('close', function() { console.log('Connection closed'); }); setTimeout(function(){ client.end('Bye bye server'); },5000);
FS Module
The FS module provides a lot of very useful functionality to access and interact with the file system. It provides methods to read and write to files in local system. Both read and write have corresponding synchronous method versions fs.readFileSync() and fs.writeFileSync() respectively.
var fs = require('fs'); // Use fs.readFile() method to read the file fs.readFile('demo.txt', "utf-8", (err, data) => { if (err) console.log(err); console.log(data.toString()); }) var data = "New File Contents"; fs.writeFile("temp.txt", data, (err) => { if (err) console.log(err); console.log("Successfully Written to File."); });
The FS module also allows to open the file using the open() method which takes various flags for different file modes. The file modes include r (read), r+ (read+write, don't create if not exist), w+ (read+write, create if not exist), a (append at end, don't create if not exist), a+ (append, create if not exist). The file can also be opened using the fs.openSync method, which returns the file descriptor, instead of providing it in a callback. There is the fs.stat() method which enables to get the details about the file.
const fs = require('fs') fs.open('test.txt', 'r', (err, fd) => { //do something })
Node JS has a path module which provides methods such as dirname() which gets file's parent directory, extname() gives file's extension, path.join() which joins two or more parts of a path and path.resolve() which returns absolute path.
Event Module
Node's event module provide EventEmitter class which allows to create/trigger events. It has two method emit() to trigger the event and on() which is used to add callback function to be executed after triggering the event.
const EventEmitter = require('events') const eventEmitter = new EventEmitter() eventEmitter.on('start', number => { console.log(`started ${number}`) }) eventEmitter.emit('start', 23)
The once() method of EventEmitter is used to subscribe, to execute the callback only for the first time when an event is triggered.
eventEmitter.once('start', (time) => { console.log('Message Received from publisher'); console.log(`${time} seconds passed since the program started`); });
Axios (Third Party Library)
Axios is a Promise based HTTP client for the browser as well as node.js. Using Promises is a great advantage when dealing with code that requires a more complicated chain of events.
var axios = require('axios'); axios.all([ axios.get('https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY&date=2017-08-03'), axios.get('https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY&date=2017-08-02') ]).then(axios.spread((response1, response2) => { console.log(response1.data.url); console.log(response2.data.url); })).catch(error => { console.log(error); });
Express Framework
Express is a minimal and flexible Node JS web application framework that provides a robust set of features to develop web and mobile applications. It facilitates the rapid development of Node based Web applications. It allows to set up middlewares to respond to HTTP Requests, enables to define routing table to perform different actions based on HTTP Method and URL and can dynamically render HTML Pages based on passing arguments to templates.
Node Package Manager (npm)
The Node Package Manager (npm) is the default package manager for Node JS and is written entirely in Javascript. The npm manages all the packages and modules for Node JS and consists of command–line client npm. It gets installed into the system with the installation of Node JS. The required packages and modules in the Node project are installed using npm.
The npm init command is used to setup a new or existing npm package. It prompts to enter the project's name, version, description, entry point (main file), test command, git repository, keywords and license.
The --yes option automatically populate all options with the default npm init values.
$ npm init --yes
The npm init command generates or updates the package.json file in the current directory. The package.json contains metadata about the project and its dependencies. It has dependencies which are used for production and devDependencies for development environment. The "engines" field in package.json specifies which versions of npm are capable of properly installing the application. The package.json also supports a scripts property that can be defined to run command-line tools that are installed in the project's local context.
The npm install or npm i command installs all the dependencies (javascript packages) listed in the package.json into the project directory. We can also pass the modules/packages to be installed to the npm install command. All the modules and dependencies are installed into the node_modules directory.
$ npm install
$ npm i
$ npm install <package>
$ npm install eslint babel-eslint
When the package is installed it is saved as a property of the dependencies field, and becomes the default in the latest version of npm.
Below are the signs that come before the semantic versions with major.minor.patch model in package.json
- ^: The latest minor release. For example, a ^1.0.4 specification might install version 1.3.0 if that's the latest minor version in the 1 major series.
- ~: latest patch release. In the same way as ^ for minor releases, ~1.0.4 specification might install version 1.0.7 if that's the latest minor version in the 1.0 minor series.
By default, npm install <package> will install the latest version of a package with the ^ version sign. An npm install within the context of an npm project will download packages into the project's node_modules folder according to package.json specifications, upgrading the package version (and in turn regenerating package-lock.json) wherever it can based on ^ and ~ version matching.
All of the exact package versions are documented in a generated package-lock.json file. The package-lock.json describes the exact versions of the dependencies used in an npm JavaScript project. It is usually generated by the npm install command.
The optional --save flag to the npm install command adds the package as a dependency entry into the project's package.json. Similarly the --save-dev flag adds the package as a devDependency to the package.json, which indicates that the packages are used for development purposes. In order to install a package without saving it in package.json, the --no-save argument is used.
$ npm install <package> --save
The npm install supports installation of packages from local directories.
$ npm install ../some-local-package
The npm can install packages in local or global mode. In local mode, it installs the package in a node_modules folder in the parent working directory. In the global mode, the packages are installed in system or user directory for e.g. "/usr/local/lib/node_modules/" directory. By default, npm installs global modules to the system directory, which requires to be authenticated as a privileged user to install global modules. It is recommended to change the default installation location from a system directory to a user directory.
Install package in global mode.
$ npm install <package> --global
$ npm install <package> -g
List all installed packages globally
$ npm list --global
We need to add ".node_modules_global/bin" to the $PATH environment variable in order to run the global packages from the command line.
The npm config lists all the configuration settings using the environment variables, npmrc files, and package.json.
$ npm config set <key> <value> [-g|--global]
$ npm config get <key>
$ npm config delete <key>
$ npm config list [-l] [--json]
$ npm config edit
The npm uninstall allows to remove a package.
$ npm uninstall underscore
In order to install a specific version of package, the @ sign is appended with the version number to the package name as below.
$ npm install underscore@1.9.1
The npm outdated command displays the Current package version installed locally, the Latest available version of the package and the Wanted version of the package that we can upgrade to without breaking our existing code.
$ npm outdated
Package Current Wanted Latest Location
underscore 1.9.1 1.9.2 1.9.2 project
The npm update command updates the outdated modules to new version.
When npm installs a package, it keeps a copy of the installed package cached within the local .npm directory, to avoid downloading it again next time. When the .npm directory gets cluttered it can be cleaned up using npm cache clean command as below.
$ ls ~/.npm
$ npm cache clean --force
npm allows developers to scan the dependencies for known security vulnerabilities using the npm audit command. The npm audit fix command automatically installs any compatible updates to vulnerable dependencies, fixing all the security vulnerabilities. The --force argument allows to upgrading packages with breaking changes.
$ npm audit
$ npm audit fix
The npm shrinkwrap is used to lock the dependency version in a project. It is ran after installing all npm packages and creates new npm-shrinkwrap.json file with information about all packages being used. It repurposes package-lock.json into a publishable npm-shrinkwrap.json or simply creates a new one. The file created and updated by this command will then take precedence over any other existing or future package-lock.json files.
$ npm shrinkwrap
The npm exec command allows to run an arbitrary command from an npm package (either one installed locally, or fetched remotely), in a similar context as running it via npm run. When running via npm exec, a double-hyphen -- flag can be used to suppress npm's parsing of switches and options that should be sent to the executed command. The below npm exec command runs "foo bar --package=@npmcli/foo" command.
$ npm exec -- foo@latest bar --package=@npmcli/foo
The "scripts" property in package.json file supports a number of built-in scripts and their preset life cycle events as well as arbitrary scripts. These all can be executed by running npm run-script <stage> or npm run <stage> as shorthand. Similar to npm exec, npm run command will run an arbitrary command from "scripts" object of package.json. If no command is provided then it will list the available scripts.
"scripts": { "lint": "eslint ./src --fix" }
$ npm run lint
The npm link command creates a global symbolic link for a dependency, which points to another directory or file on the system. In order to make our custom <module_name> available to the local project, we execute the following.
$ cd <module_name>
$ npm link
This will create a symbolic link from the global node_modules directory to the <module_name> directory. Now to use the <module_name> module in any project, we go to the project directory, then link it to the module using npm link.
$ cd <project_name>
$ npm link <module_name>
The npm publish command allows to publish a private package to npm registry i.e. npmjs.com.
$ npm publish
By default, scoped packages are published with private visibility. To publish a scoped package with public visibility, use npm publish --access public. The public package can be view by visiting https://npmjs.com/package/<package-name>.
$ npm publish --access public
The npm test command executes predefined commands specified in the "test" property of the "scripts" object in package.json.
"scripts": {
"test": "mocha"
}
$ npm test
Alternative method to install packages without updating the package.json is npm ci. It installs dependencies directly from package-lock.json and uses package.json only to validate that there are no mismatched versions. If any dependencies are missing or have incompatible versions, it will throw an error. It is meant to be used in automated environments such as test platforms, continuous integration, and deployment
$ npm ci
Node Package Execute (npx)
The npx is a tool for executing packages and comes bundled with npm version 5.2 onwards. npx is typically used for executing one-off commands, for e.g. to spin up a simple HTTP server. It allows to test and run the package, without installing anything globally. Npx executes the specified command either from a local node_modules/.bin, or from a central cache, installing any packages needed in order to run the command.
$ npx http-server
The npx binary is rewritten in npm version 7.0.0, and the standalone npx package was deprecated. The npx now uses the npm exec command under the hood for backward compatibility.
Node Version Manager
Node Version Manager (NVM) is a bash script used to manage multiple Node.js versions. It allows us to install, uninstall node.js, and switch from one version to another. nvm supports both Linux and macOS, and has a second project named nvm-windows for windows users.
$ curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.38.0/install.sh | bash
The above command will clone the nvm repository to ~/.nvm and add the source line to your profile (~/.bash_profile, ~/.zshrc, ~/.profile, or ~/.bashrc). Restart the terminal before using nvm.
Below command allows to view the list of all the available Node JS versions
$ nvm ls-remote
To install/update the most recent Node JS version we use the nvm install command.
$ nvm install node
We can also install a specific version of Node JS as below
$ nvm install v9.3.0
It allows to install a specific Node JS version and reinstall the npm global packages from a specific version.
$ nvm install v12.14.1 --reinstall-packages-from=10.18.1
We can uninstall the previously installed Node JS version as below.
$ nvm uninstall 13.6.0
To view the list of installed Node JS versions, run:
$ nvm ls
To list available remote versions on Windows 10 the command is as below.
$ nvm list
To switch through installed versions, nvm provides the nvm use command. This works similarly to the install command.
$ nvm use 13.6.0
Switch to the latest Node JS version:
$ nvm use node
To switch between different versions of Node JS
$ nvm run node v9.3.0
We can create custom aliases to identify a specific node version.
$ nvm alias awesome-version 13.6.0
$ nvm use awesome-version
We can check the current node version in use with the below nvm command.
$ nvm current
We can run a command directly for an installed version without switching the node variable.
$ nvm run 13.6.0 --version
We can run a command on a sub-shell, targeting a specific version:
$ nvm exec 13.6.0 node --version
To get the path to Node JS executable for a specific installed version, below command is used.
$ nvm which 13.6.0
Babel
Babel is a JavaScript transpiler that converts converts edge JavaScript(ES6) into plain old ES5 JavaScript that can run on any browser. Transpilers, or source-to-source compilers, are tools that read source code written in one programming language, and produce the equivalent code in another language which is in the same level, e.g. from typescript to javascript. As browsers evolve, new APIs and ECMAScript features are added. Since different browsers evolve at different speeds and prioritize different features, Babel helps to support them all and still use the modern features.
The core functionality of Babel resides at the @babel/core module. The babel-cli JavaScript package contains Babel command line tools. Install the babel-cli (babel-core can be optional) in development mode.
$ npm install babel-cli babel-core --save-dev
Install babel-preset-es2015, which is an array of plugins which allow babel to transpile ES6 JavaScript code to ES5, in devlopment mode in corresponding project.
$ npm install –-save-dev babel-cli babel-preset-es2015
Create the .babelrc file in the root directory and properties are plugins & presets. Plugin property is used to transpile specific features e.g. plugins like arrow function, classes, instanceof etc. In order to transpile all the features of ES6, presets property is used. Preset are a simple collection of babel plugins.
// projectname/.babelrc
{
"presets": ["es2015"]
}
Convert the ES6 code into ES5 by executing babel command.
$ babel src -d build
ESLint
Javascript does not deprecate old code syntax, since a lot of legacy code would be impacted which are running on JS Engines of different browsers. The new syntax is added on top of the existing syntax, were both the old and new syntax works. In order to ensure that the project does not use the old syntax and uses corresponding ECMAScript version, tools such as ESLint come in handy. ESLint is a tool for identifying and reporting on patterns found in ECMAScript/JavaScript code, with the goal of making code more consistent and avoiding bugs. Below is the setup process for ESLint. ESLint statically analyzes the code to find problems and can automatically fix many problems. ESLint is written in JavaScript on Node.js and supports React’s JSX format and ES6 features.
$ npm install eslint --save-dev
$ npx eslint --init
$ npx eslint check-this-file.js
After running eslint --init, we get a .eslintrc.{js,yml,json} file in the directory were the rules can be configured.
Yarn
Yarn is a new JavaScript package manager built solve the consistency, security and speed issues in installing packages using npm, most of which are now resolved. Yarn is only a new CLI client that fetches modules from the npm registry. Yarn can execute the tasks to install the package in parallel thus increasing performance, compared to npm which executes the tasks sequentially per package. The npm output is verbose as it recursively lists all installed packages when running npm install <package>. Yarn lists significantly less information with appropriate emojis.
Yarn can be installed using npm, which comes bundled with Node JS. Yarn can also be installed directly using corresponding system specific installation methods e.g. Ubuntu, Mac OS and Windows.
$ npm install --global yarn
$ yarn --version
Yarn writes its dependencies to package.json and stores the dependencies into node_modules directory, similar as npm. Yarn also auto-generates yarn.lock file in the current directory, which is handled entirely by Yarn. As packages/dependencies are added/upgraded/removed using the Yarn CLI, it will automatically update the yarn.lock file. The yarn.lock file includes everything Yarn needs to lock the versions for all packages in the entire dependency tree. It is recommended that yarn.lock files should be checked into source control.
The yarn init command is used to initialize a new project.
$ yarn init
The yarn or yarn install commands are used to install all the dependencies from package.json file.
$ yarn
$ yarn install
The add command is used to add a dependency to the project.
$ yarn add <package-name>
Install a package globally
$ yarn global add <package-name>
The --dev (or alias -D) flag is used to add a package as a development dependency.
$ yarn add --dev <package-name>
Upgrade a a single package or dependency
$ yarn upgrade <package-name>
Upgrade all the dependencies.
$ yarn upgrade
For selectively upgrading the package within the project, interactive upgrade command is used.
$ yarn upgrade-interactive
Remove a dependency.
$ yarn remove <package-name>
Add a global dependency.
$ yarn global add <package-name>
To check for missing packages to be installed, use yarn check command.
$ yarn check
Yarn provides a handy tool that prints the license of any dependency for the project.
$ yarn licenses ls
$ yarn licenses generate-disclaimer
Yarn helps to inspect the package and determine why a specific package was installed.
$ yarn why <package-name>
The yarn generate-lock-entry command generates a yarn.lock file based on the dependencies set in package.json. This command should be used with caution as it changes the yarn.lock file.
$ yarn generate-lock-entry
Yarn uses a global offline cache to store packages once installed, to be used as cache for new installations. Below command determines yarn's cache directory.
$ yarn cache dir
Yarn can work with multiple registry types. By default it uses npm registry (non-standard), but it can also add packages from files, remote tarballs, or remote git repositories. Below command allows to see the current configured npm registry.
$ yarn config get registry
Install local package using file.
$ yarn add file:/<path to local package directory>
Install package using remote tarball URL.
$ yarn add https://<path to compressed tarball>.tgz
Install package using remote git repository URL.
$ yarn add <git remote-url>