demo2s-2

Must Watch!



MustWatch



Bitwise NOT

The bitwise NOT is represented by a tilde (~) and simply returns the one's complement of the number. Consider this example: let num1 = 25; // binary 00000000000000000000000000011001 let num2 = ~num1; // binary 11111111111111111111111111100110 console.log(num2); // -26 Here, the bitwise NOT operator is used on 25, producing -26 as the result. This is the effect of the bitwise NOT: it negates the number and subtracts 1. The same outcome is produced with the following code: let num1 = 25; let num2 = -num1 - 1; console.log(num2); // "-26" The bitwise operation is much faster than the math operator because it works at the very lowest level of numeric representation.

Bitwise AND Operator

The bitwise AND operator is indicated by the ampersand character (&) and works on two values. The bitwise AND operator lines up the bits in each number and use the rules in the following truth table, performs an AND operation between the two bits in the same position.
Bit From First Number Bit From Second Number Result
1 1 1
1 0 0
0 1 0
0 0 0
A bitwise AND operation returns 1 if both bits are 1. It returns 0 if any bits are 0. As an example, to AND the numbers 25 and 3 together, use the following code: let result = 25 & 3; console.log(result); // 1 The result of a bitwise AND between 25 and 3 is 1. Here is how it is done: 25 = 0000 0000 0000 0000 0000 0000 0001 1001 3 = 0000 0000 0000 0000 0000 0000 0000 0011 --------------------------------------------- AND = 0000 0000 0000 0000 0000 0000 0000 0001 Only one bit (bit 0) contains a 1 in both 25 and 3. Because of this, every other bit of the resulting number is set to 0, making the result equal to 1. A bit is set in the result only if the corresponding bit is set in both operands. For example, 0x1234 & 0x00FF evaluates to 0x0034.

Bitwise OR Operator

The bitwise OR operator is represented by a single pipe character (|) and also works on two numbers. Bitwise OR follows the rules in this truth table:
Bit From First NumberBit From Second Number Result
11 1
10 1
01 1
00 0
A bitwise OR operation returns 1 if at least one bit is 1. It returns 0 only if both bits are 0. Using the same example as for bitwise AND, if you want to OR the numbers 25 and 3 together, the code looks like this: let result = 25 | 3; console.log(result); // 27 The result of a bitwise OR between 25 and 3 is 27: 25 = 0000 0000 0000 0000 0000 0000 0001 1001 3 = 0000 0000 0000 0000 0000 0000 0000 0011 --------------------------------------------- OR = 0000 0000 0000 0000 0000 0000 0001 1011 In each number, four bits are set to 1, so these are passed through to the result. The binary code 11011 is equal to 27. A bit is set in the result if the corresponding bit is set in one or both of the operands. For example, 0x1234 | 0x00FF evaluates to 0x12FF.

Bitwise XOR Operator

The bitwise XOR operator is represented by a caret (^) and also works on two values. Here is the truth table for bitwise XOR:
Bit From First Number Bit From Second Number Result
1 10
1 01
0 11
0 00
Bitwise XOR is different from bitwise OR in that it returns 1 only when exactly one bit has a value of 1. If both bits contain 1, it returns 0. To XOR the numbers 25 and 3 together, use the following code: let result = 25 ^ 3; console.log(result); // 26 The result of a bitwise XOR between 25 and 3 is 26, as shown here: 25 = 0000 0000 0000 0000 0000 0000 0001 1001 3 = 0000 0000 0000 0000 0000 0000 0000 0011 --------------------------------------------- XOR= 0000 0000 0000 0000 0000 0000 0001 1010 Four bits in each number are set to 1; however, the first bit in both numbers is 1, so that becomes 0 in the result. All of the other 1s have no corresponding 1 in the other number, so they are passed directly through to the result. The binary code 11010 is equal to 26. A bit is set in this operation's result if a corresponding bit is set in one of the two operands. For example, 0xFF00 ^ 0xF0F0 evaluates to 0x0FF0.

Left Shift Operator

The left shift is represented by two less-than signs (<<) and shifts all bits in a number to the left by the number of positions given. For example, if the number 2 (which is equal to 10 in binary) is shifted 5 bits to the left, the result is 64 (which is equal to 1000000 in binary), as shown here: let oldValue = 2; // equal to binary 10 let newValue = oldValue << 5; // equal to binary 1000000 which is decimal 64 Note that when the bits are shifted, five empty bits remain to the right of the number. The left shift fills these bits with 0s to make the result a complete 32-bit number. The left most bit is the sign bit. The number 2 00000000000000000000000000000010 The number 2 shifted to the left five bits (the number 64) 00000000000000000000000001000000 Note that left shift preserves the sign of the number it's operating on. For instance, if -2 is shifted to the left by five spaces, it becomes -64, not positive 64.

Signed Right Shift Operator

The signed right shift is represented by two greater-than signs ( >>) and shifts all bits in a 32-bit number to the right while preserving the sign (positive or negative). A signed right shift is the exact opposite of a left shift. For example, if 64 is shifted to the right five bits, it becomes 2: let oldValue = 64; // equal to binary 1000000 let newValue = oldValue >> 5; // equal to binary 10 which is decimal 2 Once again, when bits are shifted, the shift creates empty bits. This time, the empty bits occur at the left of the number but after the sign bit. ECMAScript fills these empty bits with the value in the sign bit to create a complete number.

Unsigned Right Shift Operator

The unsigned right shift is represented by three greater-than signs ( >>>) and shifts all bits in a 32-bit number to the right. For numbers that are positive, the effect is the same as a signed right shift. Using the same example as for the signed-right-shift example, if 64 is shifted to the right five bits, it becomes 2: let oldValue = 64; // equal to binary 1000000 let newValue = oldValue >>> 5; // equal to binary 10 which is decimal 2 The left most is the sign bit. The number 64 00000000000000000000000001000000 The number 64 shifted to the right five bits (the number 2) 00000000000000000000000000000010 For numbers that are negative, unlike signed right shift, the empty bits get filled with zeros regardless of the sign of the number. The unsigned-right-shift operator considers the binary representation of the negative number to be representative of a positive number instead. Because the negative number is the two's complement of its absolute value, the number becomes very large, as you can see in the following example: let oldValue = -64; // equal to binary 11111111111111111111111111000000 let newValue = oldValue >>> 5; // equal to decimal 134217726 When an unsigned right shift is used to shift -64 to the right by five bits, the result is 134217726. This happens because the binary representation of -64 is 11111111111111111111111111000000, but because the unsigned right shift treats this as a positive number, it considers the value to be 4294967232. When this value is shifted to the right by five bits, it becomes 00000111111111111111 111111111110, which is 134217726.

Boolean Logical NOT Operator

The logical NOT operator is represented by an exclamation point ( !) and may be applied to any value in ECMAScript. This operator always returns a Boolean value, regardless of the data type it's used on. The logical NOT operator first converts the operand to a Boolean value and then negates it. The logical NOT behaves in the following ways: If the operand is an object, false is returned. If the operand is an empty string, true is returned. If the operand is a nonempty string, false is returned. If the operand is the number 0, true is returned. If the operand is any number other than 0 (including Infinity), false is returned. If the operand is null, true is returned. If the operand is NaN, true is returned. If the operand is undefined, true is returned. The following example illustrates this behavior: console.log(!false); // true console.log(!"blue"); // false console.log(!0); // true console.log(!NaN); // true console.log(!""); // true console.log(!12345); // false

Convert

The logical NOT operator can also be used to convert a value into its Boolean equivalent. By using two NOT operators in a row, you can effectively simulate the behavior of the Boolean() casting function. The first NOT returns a Boolean value no matter what operand it is given. The second NOT negates that Boolean value and so gives the true Boolean value of a variable. The end result is the same as using the Boolean() function on a value, as shown here: console.log(!!"blue"); // true console.log(!!0); // false console.log(!!NaN); // false console.log(!!""); // false console.log(!!12345); // true

Boolean Logical AND Operator

The logical AND operator is represented by the double ampersand (&&) and is applied to two values, as in this example: let result = true && false; Logical AND behaves as described in the following truth table:
OPERAND 1OPERAND 2RESULT
true true true
true falsefalse
falsetrue false
falsefalsefalse
Logical AND can be used with any type of operand, not just Boolean values. When either operand is not a primitive Boolean, logical AND does not always return a Boolean value; instead, it does one of the following: If the first operand is an object, then the second operand is always returned. If the second operand is an object, then the object is returned only if the first operand evaluates to true. If both operands are objects, then the second operand is returned. If either operand is null, then null is returned. If either operand is NaN, then NaN is returned. If either operand is undefined, then undefined is returned.

Short-circuited

The logical AND operator is a short-circuited operation, meaning that if the first operand determines the result, the second operand is never evaluated. In the case of logical AND, if the first operand is false, no matter what the value of the second operand, the result can't be equal to true. Consider the following example: let found = true; let result = (found && someUndeclaredVariable); // error occurs here console.log(result); // this line never executes This code causes an error when the logical AND is evaluated, because the variable someUndeclaredVariable isn't declared. The value of the variable found is true, so the logical AND operator continued to evaluate the variable someUndeclaredVariable. When it did, an error occurred because someUndeclaredVariable is not declared and therefore cannot be used in a logical AND operation. If found is instead set to false, as in the following example, the error won't occur: let found = false; let result = (found && someUndeclaredVariable); // no error console.log(result); // works In this code, the console.log is displayed successfully. Even though the variable someUndeclaredVariable is undefined, it is never evaluated because the first operand is false. This means that the result of the operation must be false, so there is no reason to evaluate what's to the right of the &&.

Boolean Logical OR Operator

The logical OR operator is represented by the double pipe ( ||) in ECMAScript, like this: let result = true || false; Logical OR behaves as described in the following truth table:
OPERAND 1OPERAND 2 RESULT
true true true
true false true
falsetrue true
falsefalse false
If either operand is not a Boolean, logical OR will not always return a Boolean value; instead, it does one of the following: If the first operand is an object, then the first operand is returned. If the first operand evaluates to false, then the second operand is returned. If both operands are objects, then the first operand is returned. If both operands are null, then null is returned. If both operands are NaN, then NaN is returned. If both operands are undefined, then undefined is returned.

Short-circuited

The logical OR operator is short-circuited. In this case, if the first operand evaluates to true, the second operand is not evaluated. Consider this example: let found = true; let result = (found || someUndeclaredVariable); // no error console.log(result); // works As with the previous example, the variable someUndefinedVariable is undefined. However, because the variable found is set to true, the variable someUndefinedVariable is never evaluated and thus the output is "true". If the value of found is changed to false, an error occurs, as in the following example: let found = false; let result = (found || someUndeclaredVariable); // error occurs here console.log(result); // this line never executes You can also use this behavior to avoid assigning a null or undefined value to a variable. Consider the following: let myObject = preferredObject || backupObject; In this example, the variable myObject will be assigned one of two values. The preferredObject variable contains the value that is preferred if it's available, whereas the backupObject variable contains the backup value if the preferred one isn't available. If preferredObject isn't null, then it's assigned to myObject; if it is null, then backupObject is assigned to myObject. This pattern is used very frequently in ECMAScript for variable assignment.

Arithmetic Multiply Operator

The multiply operator is represented by an asterisk (*) and is used, as one might suspect, to multiply two numbers. The syntax is the same as in C, as shown here: let result = 3 * 5; The multiply operator has the following unique behaviors when dealing with special values: If the operands are numbers, regular arithmetic multiplication is performed. If the result cannot be represented by ECMAScript, either Infinity or -Infinity is returned. If either operand is NaN, the result is NaN. If Infinity is multiplied by 0, the result is NaN. If Infinity is multiplied by any finite number other than 0, the result is either Infinity or -Infinity, depending on the sign of the second operand. If Infinity is multiplied by Infinity, the result is Infinity. If either operand isn't a number, it is converted to a number behind the scenes using Number() and then the other rules are applied.

Arithmetic Divide Operator

The divide operator is represented by a slash (/) and divides the first operand by the second operand, as shown here: let result = 60 / 10; The divide operator has special behaviors for special values. They are as follows: If the operands are numbers, regular arithmetic division is performed. If the result can't be represented in ECMAScript, it returns either Infinity or -Infinity. If either operand is NaN, the result is NaN. If Infinity is divided by Infinity, the result is NaN. If zero is divided by zero, the result is NaN. If a nonzero finite number is divided by zero, the result is either Infinity or -Infinity, depending on the sign of the first operand. If Infinity is divided by any number, the result is either Infinity or -Infinity, depending on the sign of the second operand. If either operand isn't a number, it is converted to a number behind the scenes using Number() and then the other rules are applied.

Arithmetic Modulus Operator

The modulus (remainder) operator is represented by a percent sign (%) and is used in the following way: let result = 26 % 5; // equal to 1 The modulus operator behaves differently for special values, as follows: If the operands are numbers, regular arithmetic division is performed, and the remainder of that division is returned. If the dividend is an infinite number and the divisor is a finite number, the result is NaN. If the dividend is a finite number and the divisor is 0, the result is NaN. If Infinity is divided by Infinity, the result is NaN. If the dividend is a finite number and the divisor is an infinite number, then the result is the dividend. If the dividend is zero and the divisor is nonzero, the result is zero. If either operand isn't a number, it is converted to a number behind the scenes using Number() and then the other rules are applied.

Arithmetic Exponentiation Operator

New in ECMAScript 7, Math.pow() now gets its own ** operator, which behaves identically. console.log(Math.pow(3, 2); // 9 console.log(3 ** 2); // 9 console.log(Math.pow(16, 0.5); // 4 console.log(16** 0.5); // 4 The operator also gets its own exponentiate assignment operator, **=, which performs the exponentiation and subsequent assignment of the result: let squared = 3; squared **= 2; console.log(squared); // 9 let sqrt = 16; sqrt **= 0.5; console.log(sqrt); // 4

Arithmetic Add Operator

The add operator (+) is used just as one would expect, as shown in the following example: let result = 1 + 2; If the two operands are numbers, they perform an arithmetic add and return the result according to the following rules: If either operand is NaN, the result is NaN. If Infinity is added to Infinity, the result is Infinity. If -Infinity is added to -Infinity, the result is -Infinity. If Infinity is added to -Infinity, the result is NaN. If +0 is added to +0, the result is +0. If -0 is added to +0, the result is +0. If -0 is added to -0, the result is -0. If, however, one of the operands is a string, then the following rules apply: If both operands are strings, the second string is concatenated to the first. If only one operand is a string, the other operand is converted to a string and the result is the concatenation of the two strings. If either operand is an object, number, or Boolean, its toString() method is called to get a string value and then the previous rules regarding strings are applied. For undefined and null, the String() function is called to retrieve the values "undefined" and "null", respectively. Here are some examples: 1 + 2 // => 3: addition "1" + "2" // => "12": concatenation "1" + 2 // => "12": concatenation after number-to-string 1 + {} // => "1[object Object]": concatenation after object-to-string true + true // => 2: addition after boolean-to-number 2 + null // => 2: addition after null converts to 0 2 + undefined // => NaN: addition after undefined converts to NaN Consider the following: let result1 = 5 + 5; // two numbers console.log(result1); // 10 let result2 = 5 + "5"; // a number and a string console.log(result2); // "55" Normally, 5 + 5 equals 10 (a number value), as illustrated by the first two lines of code. However, if one of the operands is changed to a string, "5", the result becomes "55" because the first operand gets converted to "5" as well. Consider the following: let num1 = 5; let num2 = 10; let message = "The sum of 5 and 10 is " + num1 + num2; console.log(message); // "The sum of 5 and 10 is 510" In this example, the message variable is filled with a string that is the result of two addition operations. One might expect the final string to be "The sum of 5 and 10 is 15"; however, it actually ends up as "The sum of 5 and 10 is 510". This happens because each addition is done separately. The first combines a string with a number (5), which results in a string. The second takes that result (a string) and adds a number (10), which also results in a string. To perform the arithmetic calculation and then append that to the string, just add some parentheses like this: let num1 = 5; let num2 = 10; let message = "The sum of 5 and 10 is " + (num1 + num2); console.log(message); // "The sum of 5 and 10 is 15" Here, the two number variables are surrounded by parentheses, which instruct the interpreter to calculate its result before adding it to the string. The resulting string is "The sum of 5 and 10 is 15".

Arithmetic Subtract Operator

The subtract operator ( -) is another that is used quite frequently. Here's an example: let result = 2 - 1; The subtract operator has special rules to deal with the type conversions present in ECMAScript. They are as follows: If the two operands are numbers, perform arithmetic subtract and return the result. If either operand is NaN, the result is NaN. If Infinity is subtracted from Infinity, the result is NaN. If -Infinity is subtracted from -Infinity, the result is NaN. If -Infinity is subtracted from Infinity, the result is Infinity. If Infinity is subtracted from -Infinity, the result is -Infinity. If +0 is subtracted from +0, the result is +0. If -0 is subtracted from +0, the result is -0. If -0 is subtracted from -0, the result is +0. If either operand is a string, a Boolean, null, or undefined, it is converted to a number using Number() behind the scenes and the arithmetic is calculated using the previous rules. If that conversion results in NaN, then the result of the subtraction is NaN. If either operand is an object, its valueOf() method is called to retrieve a numeric value to represent it. If that value is NaN, then the result of the subtraction is NaN. If the object doesn't have valueOf() defined, then toString() is called and the resulting string is converted into a number. The following are some examples of these behaviors: let result1 = 5 - true; // 4 because true is converted to 1 let result2 = NaN - 1; // NaN let result3 = 5 - 3; // 2 let result4 = 5 - ""; // 5 because "" is converted to 0 let result5 = 5 - "2"; // 3 because "2" is converted to 2 let result6 = 5 - null; // 5 because null is converted to 0

Destructuring Assignment

Javascript supports a kind of compound declaration and assignment syntax known as destructuring assignment. In a destructuring assignment, the value on the right hand side of the equals sign is an array or object, and the left hand side specifies one or more variable names using a syntax that mimics array and object literal syntax. When a destructuring assignment occurs, one or more values are extracted from the value on the right and stored into the variables named on the left. Destructuring assignment is perhaps most commonly used to initialize variables as part of a const, let, or var declaration statement, but it can also be done in regular assignment expressions with variables that have already been declared. Here are simple destructuring assignments using arrays of values: let [x,y] = [1,2]; // Same as let x=1, y=2 [x,y] = [x+1,y+1]; // Same as x = x + 1, y = y + 1 [x,y] = [y,x]; // Swap the value of the two variables [x,y] // => [3,2]: the incremented and swapped values The destructuring assignment makes it easy to work with functions that return arrays of values: // Convert [x,y] coordinates to [r,theta] polar coordinates function toPolar(x, y) { return [Math.sqrt(x*x+y*y), Math.atan2(y,x)]; } let [r,theta] = toPolar(1.0, 1.0); // r == Math.sqrt(2); theta == Math.PI/4 The variables and constants can be declared as part of JavaScript's various for loops. We can use variable destructuring in this context as well. Here is a code that loops over the name/value pairs of all properties of an object and uses destructuring assignment to convert those pairs from two-element arrays into individual variables: let o = { x: 1, y: 2 }; // The object we'll loop over for(const [name, value] of Object.entries(o)) { console.log(name, value); // Prints "x 1" and "y 2" } The number of variables on the left of a destructuring assignment does not have to match the number of array elements on the right. Extra variables on the left are set to undefined, and extra values on the right are ignored. The list of variables on the left can include extra commas to skip certain values on the right: let [x,y] = [1]; // x == 1; y == undefined [x,y] = [1,2,3]; // x == 1; y == 2 [,x,,y] = [1,2,3,4]; // x == 2; y == 4 To collect all unused or remaining values into a single variable when destructuring an array, use three dots ( ...) before the last variable name on the left-hand side: let [x, ...y] = [1,2,3,4]; // y == [2,3,4] Destructuring assignment can be used with nested arrays. In this case, the left hand side of the assignment should look like a nested array literal: let [a, [b, c]] = [1, [2,2.5], 3]; // a == 1; b == 2; c == 2.5 You can use any iterable object on the righthand side of the assignment; any object that can be used with a for/of loop can also be destructured: let [first, ...rest] = "Hello"; // first == "H"; rest == ["e","l","l","o"] Destructuring assignment can also be performed when the righthand side is an object value. In this case, the left hand side of the assignment looks something like an object literal: a comma-separated list of variable names within curly braces: let transparent = {r: 0.0, g: 0.0, b: 0.0, a: 1.0}; // A RGBA color let {r, g, b} = transparent; // r == 0.0; g == 0.0; b == 0.0 The next example copies global functions of the Math object into variables, which might simplify code that does a lot of trigonometry: // Same as const sin=Math.sin, cos=Math.cos, tan=Math.tan const {sin, cos, tan} = Math; Notice in the code here that the Math object has many properties other than the three that are destructured into individual variables. Those that are not named are ignored. If the left hand side of this assignment had included a variable whose name was not a property of Math, that variable would simply be assigned undefined. In each of these object destructuring examples, we have chosen variable names that match the property names of the object we're destructuring. This keeps the syntax simple and easy to understand, but it is not required. Each of the identifiers on the left hand side of an object destructuring assignment can also be a colon-separated pair of identifiers, where the first is the name of the property whose value is to be assigned and the second is the name of the variable to assign it to: // Same as const cosine = Math.cos, tangent = Math.tan; const { cos: cosine, tan: tangent } = Math; Remember that property names are always on the left of the colon, in both object literals and on the left of an object destructuring assignment.

Conditional Property Access Operator

ES2020 adds two new kinds of property access expressions: expression ?. identifier expression ?.[ expression ] In JavaScript, the values null and undefined are the only two values that do not have properties. In a regular property access expression using . or [], you get a TypeError if the expression on the left evaluates to null or undefined. You can use ?. and ?.[] syntax to guard against errors of this type. Consider the expression a?.b. If a is null or undefined, then the expression evaluates to undefined without any attempt to access the property b. If a is some other value, then a?.b evaluates to whatever a.b would evaluate to and if a does not have a property named b, then the value will again be undefined. This form of property access expression is sometimes called "optional chaining" because it also works for longer "chained" property access expressions like this one: let a = { b: null }; let x = a.b?.c.d // => undefined console.log(x); Output:

Note

a is an object, so a.b is a valid property access expression. But the value of a.b is null, so a.b.c would throw a TypeError. By using ?. instead of . we avoid the TypeError, and a.b?.c evaluates to undefined. This means that (a.b?.c).d will throw a TypeError, because that expression attempts to access a property of the value undefined. But a.b?.c.d evaluates to undefined and does not throw an error. This is because property access with ?. is "short-circuiting": if the subexpression to the left of ?. evaluates to null or undefined, then the entire expression immediately evaluates to undefined without any further property access attempts. If a.b is an object, and if that object has no property named c, then a.b?.c.d will again throw a TypeError, and we will want to use another conditional property access: let a = { b: {} }; a.b?.c?.d // => undefined Conditional property access is also possible using ?.[] instead of []. In the expression a?.[b][c], if the value of a is null or undefined, then the entire expression immediately evaluates to undefined, and sub expressions b and c are never even evaluated. If either of those expressions has side effects, the side effect will not occur if a is not defined: let a; // we did not initialize this variable! let index = 0; try { a[index++]; // Throws TypeError } catch(e) { index // => 1: increment occurs before TypeError is thrown } a?.[index++] // => undefined: because a is undefined index // => 1: not incremented because ?.[] short-circuits a[index++] // !TypeError: can't index undefined. Conditional property access with ?. and ?.[] is one of the newest features of JavaScript.

Conditional Invocation Operator

In ES2020, you can invoke a function using ?.() instead of (). Normally when you invoke a function, if the expression to the left of the parentheses is null or undefined or any other non-function, a TypeError is thrown. With the new ?.() invocation syntax, if the expression to the left of the ?. evaluates to null or undefined, then the entire invocation expression evaluates to undefined and no exception is thrown. Before ES2020, if you wanted to write a method that takes an optional function argument, you would use an if statement to check that the function argument was defined before invoking it in the body of the if: function square(x, log) { // The second argument is an optional function if (log) { // If the optional function is passed log(x); // Invoke it } return x * x; // Return the square of the argument } With this conditional invocation syntax of ES2020, you can write the function invocation using ?.(), knowing that invocation will only happen if there is actually a value to be invoked: function square(x, log) { // The second argument is an optional function log?.(x); // Call the function if there is one return x * x; // Return the square of the argument } Note that ?.() only checks whether the left hand side is null or undefined. It does not verify that the value is actually a function. So the square() function would throw an exception if you passed two numbers to it, for example. Javascript function invocation with ?.() is short-circuiting: if the value to the left of ?. is null or undefined, then none of the argument expressions within the parentheses are evaluated: let f = null, x = 0; try { f(x++); // Throws TypeError because f is null } catch(e) { x // => 1: x gets incremented before the exception is thrown } f?.(x++) // => undefined: f is null, but no exception thrown x // => 1: increment is skipped because of short-circuiting Conditional invocation expressions with ?.() work just as well for methods as they do for functions. Consider the following expressions: o.m() // Regular property access, regular invocation o?.m() // Conditional property access, regular invocation o.m?.() // Regular property access, conditional invocation In the first expression, o must be an object with a property m and the value of that property must be a function. In the second expression, if o is null or undefined, then the expression evaluates to undefined. But if o has any other value, then it must have a property m whose value is a function. And in the third expression, o must not be null or undefined. If it does not have a property m, or if the value of that property is null, then the entire expression evaluates to undefined.

in Operator

The in operator expects a left-side operand that is a string, symbol, or value that can be converted to a string. It expects a right-side operand that is an object. It evaluates to true if the left-side value is the name of a property of the right-side object. For example: let point = {x: 1, y: 1}; // Define an object "x" in point // => true: object has property named "x" "z" in point // => false: object has no "z" property. "toString" in point // => true: object inherits toString method let data = [7,8,9]; // An array with elements (indices) 0, 1, and 2 "0" in data // => true: array has an element "0" 1 in data // => true: numbers are converted to strings 3 in data // => false: no element 3

instanceof Operator

The instanceof operator expects a left-side operand that is an object and a right-side operand that identifies a class of objects. The operator evaluates to true if the left-side object is an instance of the right-side class and evaluates to false otherwise. In JavaScript, classes of objects are defined by the constructor function that initializes them. Thus, the right-side operand of instanceof should be a function. Here are examples: Create a new object with the Date() constructor. let d = new Date(); Apply instanceof operator d instanceof Date // => true: d was created with Date() d instanceof Object // => true: all objects are instances of Object d instanceof Number // => false: d is not a Number object Create an array with array literal syntax let a = [1, 2, 3]; Apply instanceof operator a instanceof Array // => true: a is an array a instanceof Object // => true: all arrays are objects a instanceof RegExp // => false: arrays are not regular expressions Note that all objects in Javascript are instances of Object. instanceof considers the "superclasses" when deciding whether an object is an instance of a class. If the left-side operand of instanceof is not an object, instanceof returns false. If the right hand side is not a class of objects, it throws a TypeError.

typeof Operator

typeof is a unary operator that is placed before its single operand, which can be of any type. Its value is a string that specifies the type of the operand. The following table specifies the value of the typeof operator for any JavaScript value.
x typeof x
undefined "undefined"
null "object"
true or false "boolean"
any number or NaN "number"
any BigInt "bigint"
any string "string"
any symbol "symbol"
any function "function"
any non function object "object"
You might use the typeof operator in an expression like this: If the value is a string, wrap it in quotes, otherwise, convert (typeof value === "string") ? "'" + value + "'" : value.toString() Note that typeof returns "object" if the operand value is null. If you want to distinguish null from objects, you'll have to explicitly test for this special-case value. Although JavaScript functions are a kind of object, the typeof operator considers functions to be sufficiently different that they have their own return value.

delete Operator

delete is a unary operator that attempts to delete the object property or array ele ment specified as its operand. delete is typically used for its property deletion. Delete property: let o = { x: 1, y: 2}; // Start with an object delete o.x; // Delete one of its properties "x" in o // => false: the property does not exist anymore Delete array element let a = [1,2,3]; // Start with an array delete a[2]; // Delete the last element of the array 2 in a // => false: array element 2 doesn't exist anymore a.length // => 3: note that array length doesn't change, though A deleted property or array element is not merely set to the undefined value. When a property is deleted, the property ceases to exist. Attempting to read a nonexistent property returns undefined, but you can test for the actual existence of a property with the in operator. Deleting an array element leaves a "hole" in the array and does not change the array's length. The resulting array is sparse. Here are some example uses of the delete operator: let o = {x: 1, y: 2}; delete o.x; // Delete one of the object properties; returns true. typeof o.x; // Property does not exist; returns "undefined". delete o.x; // Delete a nonexistent property; returns true. delete 1; // This makes no sense, but it just returns true. Can't delete a variable; returns false, or SyntaxError in strict mode. delete o; Undeletable property: returns false, or TypeError in strict mode.

if Statement

The Javascript if statement has the following syntax: if (condition) statement1 else statement2 The condition can be any expression; it doesn't even have to evaluate to an actual Boolean value. ECMAScript automatically converts the result of the expression into a Boolean by calling the Boolean() casting function on it. If the condition evaluates to true, statement1 is executed; if the condition evaluates to false, statement2 is executed. Each of the statements can be either a single line or a code block. Consider this example: if (i > 25) console.log("Greater than 25."); // one-line statement else { console.log("Less than or equal to 25."); // block statement } It's the best coding practice to always use block statements, even if only one line of code is to be executed. You can also chain if statements together like so: if (condition1) statement1 else if (condition2) statement2 else statement3 Here's an example: if (i > 25) { console.log("Greater than 25."); } else if (i < 0) { console.log("Less than 0."); } else { console.log("Between 0 and 25, inclusive."); }

do-while Statement

The do-while statement is a post-test loop, meaning that the escape condition is evaluated only after the code inside the loop has been executed. The body of the loop is always executed at least once before the expression is evaluated. Here's the syntax: do { statement } while (expression); And here's an example of its usage: let i = 0; do { i += 2; } while (i < 10); In this example, the loop continues as long as i is less than 10. The variable starts at 0 and is incremented by two each time through the loop.

while Statement

The while statement is a pretest loop. The escape condition is evaluated before the code inside the loop has been executed. It is possible that the body of the loop is never executed. Here's the syntax: while(expression) statement And here's an example of its usage: let i = 0; while (i < 10) { i += 2; } In this example, the variable i starts out equal to 0 and is incremented by two each time through the loop. As long as the variable is less than 10, the loop will continue.

for Statement

The for statement adds capabilities of variable initialization before entering the loop and defining post loop code to be executed. Here's the syntax: for (initialization; expression; post-loop-expression) statement And here's an example of its usage: let count = 10; for (let i = 0; i < count; i++) { console.log(i); } This code defines a variable i that begins with the value 0. The for loop is entered only if the conditional expression (i < count) evaluates to true, making it possible that the body of the code might not be executed. If the body is executed, the post loop expression is also executed, iterating the variable i. This for loop is the same as the following: let count = 10; let i = 0; while (i < count) { console.log(i); i++; } Nothing can be done with a for loop that can't be done using a while loop. The for loop simply encapsulates the loop-related code into a single location. The majority of the time you will find that the iterator variable is not useful after the loop completes. In these cases, the cleanest implementation is to use a let declaration inside the loop initialization to declare the iterator variable because its scope will be limited to only the loop itself. The initialization, control expression, and post loop expression are all optional. You can create an infinite loop by omitting all three, like this: for (;;) { // infinite loop doSomething(); } Including only the control expression effectively turns a for loop into a while loop, as shown here: let count = 10; let i = 0; for (; i < count; ) { console.log(i); i++; } This versatility makes the for statement one of the most used in the language.

for-in Statement

The for-in statement is a strict iterative statement. It is used to enumerate the non-symbol keyed properties of an object. Here's the syntax: for (property in expression) statement You might use a for/in loop like this: for(let p in o) { // Assign property names of o to variable p console.log(o[p]); // Print the value of each property } To execute a for/in statement, the JavaScript interpreter first evaluates the object expression. If it evaluates to null or undefined, the interpreter skips the loop and moves on to the next statement. And here's an example of its usage: for (const propName in window) { document.write(propName); } Here, the for-in statement is used to display all the properties of the BOM window object. Each time through the loop, the propName variable is filled with the name of a property that exists on the window object. This continues until all of the available properties have been enumerated. The const operator in the control statement is not necessary but is recommended for ensuring the use of a local variable that will not be altered. Object properties in ECMAScript are unordered, so the order in which property names are returned in a for-in statement cannot necessarily be predicted. All enumerable properties will be returned once, but the order may differ across browsers. The for-in statement doesn't execute the body of the loop if the variable representing the object to iterate over is null or undefined.

for-of Statement

The for-of statement is a strict iterative statement. It is used to loop through elements in an iterable object. Here's the syntax: for (property of expression) statement And here's an example of its usage: for (const el in [2,4,6,8) { document.write(el); } Here, the for-of statement is used to display all the elements inside the four-element array. This continues until each element in the array has been looped over. The const operator in the control statement is not necessary but is recommended for ensuring the use of a local variable that will not be altered. The for-of loop will iterate in the order that the iterable produces values via its next() method. The for-of statement will throw an error if the entity that it is attempting to iterate over does not support iteration. In ES2018, the for-of statement is extended as a for-await-of loop to support async iterable which produce promises.

for-of on object

Objects are not (by default) iterable. Attempting to use for/of on a regular object throws a TypeError at runtime: let o = { x: 1, y: 2, z: 3 }; for(let element of o) { // Throws TypeError because o is not iterable console.log(element); } If you want to iterate through the properties of an object, you can use the for/in loop, or use for/of with the Object.keys() method: let o = { x: 1, y: 2, z: 3 }; let keys = ""; for(let k of Object.keys(o)) { keys += k; } keys // => "xyz" This works because Object.keys() returns an array of property names for an object, and arrays are iterable with for/of. This iteration of the keys of an object is not live as the array example above was. Your changes to the object o made in the loop body will have no effect on the iteration. If you don't care about the keys of an object, you can also iterate through their corresponding values like this: let sum = 0; for(let v of Object.values(o)) { sum += v; } sum // => 6 And if you are interested in both the keys and the values of an object's properties, you can use for/of with Object.entries() and destructuring assignment: let pairs = ""; for(let [k, v] of Object.entries(o)) { pairs += k + v; } pairs // => "x1y2z3" Object.entries() returns an array of arrays, where each inner array represents a key/value pair for one property of the object. We use destructuring assignment in this code example to unpack those inner arrays into two individual variables.

for-of on String

Strings are iterable character-by-character in ES6: let frequency = {}; for(let letter of "mississippi") { if (frequency[letter]) { frequency[letter]++; } else { frequency[letter] = 1; } } frequency // => {m: 1, i: 4, s: 4, p: 2}

for/of with Set and Map

The built-in ES6 Set and Map classes are iterable. When you iterate a Set with for/of, the loop body runs once for each element of the set. You could use code like this to print the unique words in a string of text: let text = "Na na na na na na na na Batman!"; let wordSet = new Set(text.split(" ")); let unique = []; for(let word of wordSet) { unique.push(word); } unique // => ["Na", "na", "Batman!"] The iterator for a Map object does not iterate the Map keys, or the Map values, but key/value pairs. Each time through the iteration, the iterator returns an array whose first element is a key and whose second element is the corresponding value. Given a Map m, you could iterate and destructure its key/value pairs like this: let m = new Map([[1, "one"]]); for(let [key, value] of m) { key // => 1 value // => "one" }

Labeled Statements

It is possible to label statements for later use with the following syntax: label: statement Here's an example: start: for (let i = 0; i < count; i++) { console.log(i); } In this example, the label start can be referenced later by using the break or continue statement. Labeled statements are typically used with nested loops.

break Statements

The break and continue statements provide stricter control over the execution of code in a loop. The break statement exits the loop immediately, forcing execution to continue with the next statement after the loop. Here's an example: let num = 0; for (let i = 1; i < 10; i++) { if (i % 5 == 0) { break; } num++; } console.log(num); // 4 In this code, the for loop increments the variable i from 1 to 10. In the body of a loop, an if statement checks to see if the value of i is evenly divisible by 5 (using the modulus operator). If so, the break statement is executed and the loop is exited. The num variable starts out at 0 and indicates the number of times the loop has been executed. After the break statement has been hit, the next line of code to be executed is the console.log, which displays 4. The number of times the loop has been executed is four because when i equals 5, the break statement causes the loop to be exited before num can be incremented. Both the break and continue statements can be used in conjunction with labeled statements to return to a particular location in the code. This is typically used when there are loops inside of loops, as in the following example: let num = 0; outermost: for (let i = 0; i < 10; i++) { for (let j = 0; j < 10; j++) { if (i == 5 && j == 5) { break outermost; } num++; } } console.log(num); // 55 In this example, the outermost label indicates the first for statement. Each loop normally executes 10 times, meaning that the num++ statement is normally executed 100 times and, consequently, num should be equal to 100 when the execution is complete. The break statement here is given one argument: the label to break to. Adding the label allows the break statement to break not just out of the inner for statement (using the variable j) but also out of the outer for statement (using the variable i). Because of this, num ends up with a value of 55, because execution is halted when both i and j are equal to 5. In this example, the outermost label indicates the first for statement. Each loop normally executes 10 times, meaning that the num++ statement is normally executed 100 times and, consequently, num should be equal to 100 when the execution is complete. The break statement here is given one argument: the label to break to. Adding the label allows the break statement to break not just out of the inner for statement (using the variable j) but also out of the outer for statement (using the variable i). Because of this, num ends up with a value of 55, because execution is halted when both i and j are equal to 5.

continue Statements

The break and continue statements provide stricter control over the execution of code in a loop. The continue statement exits the loop immediately, but execution continues from the top of the loop. Consider the following code let num = 0; for (let i = 1; i < 10; i++) { if (i % 5 == 0) { continue; } num++; } console.log(num); // 8 Here, the console.log displays 8, the number of times the loop has been executed. When i reaches a value of 5, the loop is exited before num is incremented, but execution continues with the next iteration, when the value is 6. The loop then continues until its natural completion, when i is 10. The final value of num is 8 instead of 9 because one increment didn't occur due to the continue statement. The continue statement can be used with a label, as shown in the following example: let num = 0; outermost: for (let i = 0; i < 10; i++) { for (let j = 0; j < 10; j++) { if (i == 5 && j == 5) { continue outermost; } num++; } } console.log(num); // 95 In this case, the continue statement forces execution to continue-not in the inner loop but in the outer loop. When j is equal to 5, continue is executed, which means that the inner loop misses five iterations, leaving num equal to 95.

with Statement

The with statement sets the scope of the code within a particular object. The syntax is as follows: with (expression) statement; The with statement was created as a convenience for times when a single object was being coded to over and over again, as in this example: let qs = location.search.substring(1); let hostName = location.hostname; let url = location.href; Here, the location object is used on every line. This code can be rewritten using the with statement as follows: with(location) { let qs = search.substring(1); let hostName = hostname; let url = href; } In this rewritten version of the code, the with statement is used in conjunction with the location object. This means that each variable inside the statement is first considered to be a local variable. If it's not found to be a local variable, the location object is searched to see if it has a property of the same name. If so, then the variable is evaluated as a property of location. In strict mode, the with statement is not allowed and is considered a syntax error.

switch Statement

The Javascript switch statement is another flow-control statement adopted from other languages. The syntax for the switch statement in ECMAScript closely resembles the syntax in other C-based languages, as you can see here: switch (expression) { case value1: statement break; case value2: statement break; case value3: statement break; case value4: statement break; default: statement } If the expression is equal to the value, execute the statement. The break keyword causes code execution to jump out of the switch statement. Without the break keyword, code execution falls through the original case into the following one. The default keyword marks what is to be done if the expression does not evaluate to one of the cases. In effect, it is an else statement. For the following for statement: if (i == 25) { console.log("25"); } else if (i == 35) { console.log("35"); } else if (i == 45) { console.log("45"); } else { console.log("Other"); } The equivalent switch statement is as follows: switch (i) { case 25: console.log("25"); break; case 35: console.log("35"); break; case 45: console.log("45"); break; default: console.log("Other"); } It's best to always put a break statement after each case to avoid having cases fall through into the next one. If you need a case statement to fall through, include a comment indicating that the omission of the break statement is intentional, such as this: switch (i) { case 25: /* falls through */ case 35: console.log("25 or 35"); break; case 45: console.log("45"); break; default: console.log("Other"); } ECMAScript switch statement works with all data types including strings and objects. The case values need not be constants: they can be variables and even expressions. Consider the following example: switch ("hello world") { case "hello" + " world": console.log("hi."); break; case "goodbye": console.log("hey."); break; default: console.log("Unexpected message."); } In this example, a string value is used in a switch statement. The first case is actually an expression that evaluates a string concatenation. Because the result of this concatenation is equal to the switch argument, the console.log displays "hi." We can use case expressions to check a range of value: let num = 25; switch (true) { case num < 0: console.log("Less than 0."); break; case num >= 0 && num <= 10: console.log("Between 0 and 10."); break; case num > 10 && num <= 20: console.log("Between 10 and 20."); break; default: console.log("More than 20."); } Here, a variable num is defined outside the switch statement. The expression passed into the switch statement is true because each case is a conditional that will return a Boolean value. Each case is evaluated, in order, until a match is found or until the default statement is encountered. The switch statement compares values using the identically equal operator, so no type coercion occurs. For example, the string "10" is not equal to the number 10.

throw Statement

An exception is a signal that indicates that some sort of exceptional condition or error has occurred. To throw an exception is to signal such an error or exceptional condition. To catch an exception is to handle it-to take whatever actions are necessary or appropriate to recover from the exception. In JavaScript, exceptions are thrown whenever a runtime error occurs and whenever the program explicitly throws one using the throw statement. Exceptions are caught with the try/catch/finally statement. The throw statement has the following syntax: throw expression; expression may evaluate to a value of any type. You might throw a number that represents an error code or a string that contains a human-readable error message. The Error class and its subclasses are used when the JavaScript interpreter itself throws an error, and you can use them as well. An Error object has a name property that specifies the type of error and a message property that holds the string passed to the constructor function. Here is an example function that throws an Error object when invoked with an invalid argument: function factorial(x) { // If the input argument is invalid, throw an exception! if (x < 0) throw new Error("x must not be negative"); // Otherwise, compute a value and return normally let f; for(f = 1; x > 1; f *= x, x--) /* empty */ ; return f; } factorial(4) // => 24 When an exception is thrown, the JavaScript interpreter immediately stops normal program execution and jumps to the nearest exception handler. Exception handlers are written using the catch clause of the try/catch/finally statement. If the block of code in which the exception was thrown does not have an associated catch clause, the interpreter checks the next-highest enclosing block of code to see if it has an exception handler associated with it. This continues until a handler is found. If an exception is thrown in a function that does not contain a try/catch/finally statement to handle it, the exception propagates up to the code that invoked the function. In this way, exceptions propagate up through the lexical structure of JavaScript methods and up the call stack. If no exception handler is ever found, the exception is treated as an error and is reported to the user.

try/catch/finally Statement

The try/catch/finally statement is JavaScript's exception handling mechanism. The try clause defines the block of code whose exceptions are to be handled. The try block is followed by a catch clause, which is a block of statements that are invoked when an exception occurs within the try block. The catch clause is followed by a finally block containing cleanup code that is guaranteed to be executed, regardless of what happens in the try block. Both the catch and finally blocks are optional, but a try block must be accompanied by at least one of these blocks. The try, catch, and finally blocks all begin and end with curly braces. These braces are a required part of the syntax and cannot be omitted, even if a clause contains only a single statement. The following code illustrates the syntax and purpose of the try/catch/finally statement: try { statement; statement; ... } catch(e) { handler; handler; ... } finally { clean up; ... } Note that the catch keyword is generally followed by an identifier in parentheses. This identifier is like a function parameter. When an exception is caught, the value associated with the exception, which could be an Error object, for example, is assigned to this parameter. The identifier associated with a catch clause has block scope-it is only defined within the catch block.

Example

Here is a realistic example of the try/catch statement. It uses the factorial() method and the client-side JavaScript methods prompt() and alert() for input and output: try {/* w w w . d e mo 2 s . c o m */ let n = -1; // Compute the factorial of the number, assuming the input is valid let f = factorial(n); // Display the result console.log(n + "! = " + f); } catch(ex) { // If the user's input was not valid, we end up here console.log(ex); // Tell the user what the error is } function factorial(x) { // If the input argument is invalid, throw an exception! if (x < 0) throw new Error("x must not be negative"); // Otherwise, compute a value and return normally let f; for(f = 1; x > 1; f *= x, x--) /* empty */ ; return f; } This example is a try/catch statement with no finally clause. Although finally is not used as often as catch, it can be useful. The finally clause is guaranteed to be executed if any portion of the try block is executed, regardless of how the code in the try block completes. It is generally used to clean up after the code in the try clause. In the normal case, the JavaScript interpreter reaches the end of the try block and then proceeds to the finally block, which performs any necessary cleanup. If the interpreter left the try block because of a return, continue, or break statement, the finally block is executed before the interpreter jumps to its new destination. If an exception occurs in the try block and there is an associated catch block to han dle the exception, the interpreter first executes the catch block and then the finally block. If there is no local catch block to handle the exception, the interpreter first executes the finally block and then jumps to the nearest containing catch clause. If a finally block itself causes a jump with a return, continue, break, or throw statement, or by calling a method that throws an exception, the interpreter abandons whatever jump was pending and performs the new jump. For example, if a finally clause throws an exception, that exception replaces any exception that was in the process of being thrown. If a finally clause issues a return statement, the method returns normally, even if an exception has been thrown and has not yet been handled. try and finally can be used together without a catch clause. In this case, the finally block is simply cleanup code that is guaranteed to be executed, regardless of what happens in the try block.

catch clauses

Sometime we use a catch clause to detect and stop the propagation of an exception, and we do not care about the type or the value of the exception. In ES2019 and later, you can omit the parentheses and the identifier and use the catch keyword bare in this case. Here is an example: // Like JSON.parse(), but return undefined instead of throwing an error function parseJSON(s) { try { return JSON.parse(s); } catch { // Something went wrong but we don't care what it was return undefined; } }

Functions Introduction

Functions in ECMAScript are declared using the function keyword, followed by a set of arguments and then the body of the function. The basic function syntax is as follows: function functionName(arg0, arg1,...,argN) { statements } Here's an example: function sayHi(name, message) { console.log("Hello " + name + ", " + message); } This function can then be called by using the function name, followed by the function arguments enclosed in parentheses and separated by commas, if there are multiple arguments. The code to call the sayHi() function looks like this: sayHi("CSS", "how are you today?"); The output of this function call is, "Hello CSS, how are you today?" The named arguments name and message are used as part of a string concatenation that is ultimately displayed in an console.log. Functions in ECMAScript need not specify whether they return a value. Any function can return a value at any time by using the return statement followed by the value to return. Consider this example: function sum(num1, num2) { return num1 + num2; } The sum() function adds two values together and returns the result. Aside from the return statement, there is no special declaration indicating that the function returns a value. This function can be called using the following: const result = sum(5, 10); A function stops executing and exits immediately when it encounters the return statement. Any code that comes after a return statement will never be executed. For example: function sum(num1, num2) { return num1 + num2; console.log("Hello world"); // never executed } In this example, the console.log will never be displayed because it appears after the return statement. It's also possible to have more than one return statement in a function, like this: function diff(num1, num2) { if (num1 < num2) { return num2 - num1; } else { return num1 - num2; } } Here, the diff() function determines the difference between two numbers. If the first number is less than the second, it subtracts the first from the second; otherwise it subtracts the second from the first. Each branch of the code has its own return statement that does the correct calculation. The return statement can also be used without specifying a return value. When used in this way, the function stops executing immediately and returns undefined as its value. This is typically used in functions that don't return a value to stop function execution early, as in the following example, where the console.log won't be displayed: function sayHi(name, message) { return; console.log("Hello " + name + ", " + message); // never called } Strict mode places several restrictions on functions: No function can be named eval or arguments. No named parameter can be named eval or arguments. No two named parameters can have the same name. If any of these occur, it's considered a syntax error and the code will not execute.

Function Syntax

ECMAScript functions actually are objects. Each function is an instance of the Function type that has properties and methods just like any other reference type. Because functions are objects, function names are simply pointers to function objects and are not necessarily tied to the function itself.

Function-declaration syntax

Functions are typically defined using function-declaration syntax, as in this example: function sum (num1, num2) { return num1 + num2; } In this code, a variable sum is defined and initialized to be a function. There is no name included after the function keyword because it's not needed-the function can be referenced by the variable sum. There is no semicolon after the end of the function definition. The function-declaration syntax is almost exactly equivalent to using a function expression, such as this: let sum = function(num1, num2) { return num1 + num2; }; There is a semicolon after the end of the function, just as there would be after any variable initialization.

Arrow function

We can use the "arrow" function syntax, such as this: let sum = (num1, num2) => { return num1 + num2; };

Constructor

The final way to define functions is to use the Function constructor, which accepts any number of arguments. The last argument is always considered to be the function body, and the previous arguments enumerate the new function's arguments. Consider this example: // not recommended let sum = new Function("num1", "num2", "return num1 + num2"); This syntax is not recommended because it causes a double interpretation of the code. Once for the regular ECMAScript code and once for the strings that are passed into the constructor and thus can affect performance.

Note

It's important to think of functions as objects and function names as pointers-this syntax is great at representing that concept.

Arrow Functions

ECMAScript 6 can define a function expression using the fat-arrow syntax. For the most part, arrow functions instantiate function objects that behave in the same manner as their formal function expression counterparts. Anywhere a function expression can be used, an arrow function can also be used: let arrowSum = (a, b) => { return a + b; }; let functionExpressionSum = function(a, b) { return a + b; }; console.log(arrowSum(5, 8)); // 13 console.log(functionExpressionSum(5, 8)); // 13 Output: Arrow functions are useful in inline situations where they offer a more succinct syntax: let ints = [1, 2, 3]; console.log(ints.map(function(i) { return i + 1; })); // [2, 3, 4] console.log(ints.map((i) => { return i + 1 })); // [2, 3, 4] Output: Arrow functions do not require the parentheses if you only want to use a single parameter. If you want to have zero parameters, or more than one parameter, parentheses are required: Both of the following are valid let double = (x) => { return 2 * x; }; let triple = x => { return 3 * x; }; Zero parameters require an empty pair of parentheses let getRandom = () => { return Math.random(); }; Multiple parameters require parentheses let sum = (a, b) => { return a + b; }; Invalid syntax: let multiply = a, b => { return a * b; };//error

Curly braces

Arrow functions also do not require curly braces, but choosing to not use them changes the behavior of the function. Using the curly braces is called the "block body" syntax. It behaves in the same way as a normal function expression in that multiple lines of code can exist inside the arrow function. If you omit the curly braces, you are using the "concise body" syntax and are limited to a single line of code, such as an assignment or expression. The value of this line will implicitly return, as demonstrated here: // Both are valid and will return the value let double = (x) => { return 2 * x; }; let triple = (x) => 3 * x; Assignment is allowed let value = {}; let setName = (x) => x.name = "HTML"; setName(value); console.log(value.name); // "HTML" Invalid syntax:

Note

Arrow functions are not suited in several situations. They do not allow the use of arguments, super, or new.target, and cannot be used as a constructor. Additionally, function objects created with the arrow syntax do not have a prototype defined.

Function Names

Because function names are simply pointers to functions, they act like any other variable containing a pointer to an object. It's possible to have multiple names for a single function, as in this example: function sum(num1, num2) { return num1 + num2; } console.log(sum(10, 10)); // 20 let anotherSum = sum; console.log(anotherSum(10, 10)); // 20 sum = null; console.log(anotherSum(10, 10)); // 20 This code defines a function named sum() that adds two numbers together. A variable, anotherSum, is declared and set equal to sum. Note that using the function name without parentheses accesses the function pointer instead of executing the function. At this point, both anotherSum and sum point to the same function, meaning that anotherSum() can be called and a result returned. When sum is set to null, it stops its relationship with the function, although anotherSum() can still be called without any problems. All function objects in ECMAScript 6 expose a read-only name property that describes the function. In many cases, this will just be the function identifier, or the variable name that references the function. If a function is unnamed, it will be reported as such. If it is created using the function constructor, it will be identified as "anonymous": function foo() {} let bar = function() {}; let baz = () => {}; console.log(foo.name); // foo console.log(bar.name); // bar console.log(baz.name); // baz console.log((() => {}).name); // (empty string) console.log((new Function()).name); // anonymous If a function is a getter, a setter, or instantiated using bind(), a prefix will be prepended to identify it as such: function foo() {} console.log(foo.bind(null).name); // bound foo let Person = { years: 1, get age() { return this.years; }, set age(newAge) { this.years = newAge; } } let propertyDescriptor = Object.getOwnPropertyDescriptor(Person, 'age'); console.log(propertyDescriptor.get.name); // get age console.log(propertyDescriptor.set.name); // set age Output:

Function Arguments

Function arguments in ECMAScript don't behave in the same way as function arguments in most other languages. An ECMAScript function doesn't care how many arguments are passed in, nor does it care about the data types of those arguments. Just because you define a function to accept two arguments doesn't mean you can pass in only two arguments. You could pass in one or three or none, and the interpreter won't complain. The function arguments in ECMAScript are represented as an array internally. The array is always passed to the function, but the function doesn't care what is in the array. In fact, when a function is defined using the function keyword, not arrow function, there actually is an arguments object that can be accessed while inside a function to retrieve the values of each argument that was passed in. The arguments object acts like an array in that you can access each argument using bracket notation, the first argument is arguments[0], the second is arguments[1], and so on, We can also determine how many arguments were passed in by using the length property. In the following example, the sayHi() function's first argument is named name. function sayHi(name, message) { console.log("Hello " + name + ", " + message); } The same value can be accessed by referencing arguments[0]. Therefore, the function can be rewritten without naming the arguments explicitly, like this: function sayHi() { console.log("Hello " + arguments[0] + ", " + arguments[1]); } In this rewritten version, there are no named arguments. The name and message arguments have been removed, yet the function will behave appropriately. This illustrates an important point about functions in ECMAScript: named arguments are a convenience, not a necessity. Naming your arguments in ECMAScript does not create a function signature that must be matched later on and there is no validation against named arguments.

Length

The arguments object can also be used to check the number of arguments passed into the function via the length property. The following example outputs the number of arguments passed into the function each time it is called: function howManyArgs() { console.log(arguments.length); } howManyArgs("string", 45); // 2 howManyArgs(); // 0 howManyArgs(12); // 1 This example shows alerts displaying 2, 0, and 1. In this way, developers have the freedom to let functions accept any number of arguments and behave appropriately. Consider the following: function doAdd() { if (arguments.length === 1) { console.log(arguments[0] + 10); } else if (arguments.length === 2) { console.log(arguments[0] + arguments[1]); } } doAdd(10); // 20 doAdd(30, 20); // 50 The function doAdd() adds 10 to a number only if there is one argument; if there are two arguments, they are simply added together and returned. So doAdd(10) returns 20, whereas doAdd(30,20) returns 50. It's not quite as good as overloading, but it is a sufficient workaround for this ECMAScript limitation. The arguments object can be used in conjunction with named arguments, such as the following: function doAdd(num1, num2) { if (arguments.length === 1) { console.log(num1 + 10); } else if (arguments.length === 2) { console.log(arguments[0] + num2); } } In this rewrite of the doAdd() function, two-named arguments are used in conjunction with the arguments object. The named argument num1 holds the same value as arguments[0], so they can be used interchangeably. The same is true for num2 and arguments[1]. The values of arguments always stay in sync with the values of the corresponding named parameters. For example: function doAdd(num1, num2) { arguments[1] = 10; console.log(arguments[0] + num2); } This version of doAdd() always overwrites the second argument with a value of 10. Because values in the arguments object are automatically reflected by the corresponding named arguments, the change to arguments[1] also changes the value of num2, so both have a value of 10. This doesn't mean that both access the same memory space, however; their memory spaces are separate but happen to be kept in sync. This effect goes only one way: changing the named argument does not result in a change to the corresponding value in arguments. If only one argument is passed in, then setting arguments[1] to a value will not be reflected by the named argument because the length of the arguments object is set based on the number of arguments passed in, not the number of named arguments listed for the function. Any named argument that is not passed into the function is automatically assigned the value undefined. This is the same as defining a variable without initializing it. For example, if only one argument is passed into the doAdd() function, then num2 has a value of undefined.

Strict mode

Strict mode makes several changes to how the arguments object can be used. First, assignment like arguments[1] = 10; no longer works. The value of num2 remains undefined even though arguments[1] has been assigned to 10. Second, trying to overwrite the value of arguments is a syntax error.

Arguments in Arrow Functions

When a function is defined using the arrow notation, the arguments passed to the function cannot be accessed using the arguments keyword; they can only be accessed using their named token in the function definition. function foo() { console.log(arguments[0]); } foo(5); // 5 let bar = () => { console.log(arguments[0]); }; bar(5); Output: Although the arrow function arguments may not be available, it is possible that the arguments keyword is provided to the arrow function scope from the scope of a wrapping function being invoked: function foo() { let bar = () => { console.log(arguments[0]); // 5 }; bar(); } foo(5); NOTE All arguments in ECMAScript are passed by value. It is not possible to pass arguments by reference. If an object is passed as an argument, the value is just a reference to the object.

Function arguments object

The arguments object is an array-like object that contains all of the arguments that were passed into the function. It is only available when a function is declared using the function keyword. The arrow function does not have arguments object. Though its primary use is to represent function arguments, the arguments object also has a property named callee, which is a pointer to the function that owns the arguments object. Consider the following classic factorial function: function factorial(num) { if (num <= 1) { return 1; } else { return num * factorial(num - 1); } } Factorial functions are typically defined to be recursive, as in this example, which works fine when the name of the function is set and won't be changed. However, the proper execution of this function is tightly coupled with the function name "factorial". It can be decoupled by using arguments.callee as follows: function factorial(num) { if (num <= 1) { return 1; } else { return num * arguments.callee(num - 1); } } In this rewritten version of the factorial() function, there is no longer a reference to the name "factorial" in the function body, which ensures that the recursive call will happen on the correct function no matter how the function is referenced. Consider the following: let trueFactorial = factorial; factorial = function() { return 0; }; console.log(trueFactorial(5)); // 120 console.log(factorial(5)); // 0 Here, the variable trueFactorial is assigned the value of factorial, effectively storing the function pointer in a second location. The factorial variable is then reassigned to a function that simply returns 0. Without using arguments.callee in the original factorial() function's body, the call to trueFactorial() would return 0. However, with the function decoupled from the function name, trueFactorial() correctly calculates the factorial, and factorial() is the only function that returns 0.

Function length property

Functions are objects in ECMAScript and therefore have properties and methods. Each function has two properties: length and prototype. The length property indicates the number of named arguments that the function expects, as in this example: function sayName(name) { console.log(name); } function sum(num1, num2) { return num1 + num2; } function sayHi() { console.log("hi"); } console.log(sayName.length); // 1 console.log(sum.length); // 2 console.log(sayHi.length); // 0 This code defines three functions, each with a different number of named arguments. The sayName() function specifies one argument, so its length property is set to 1. The sum() function specifies two arguments, so its length property is 2, and sayHi() has no named arguments, so its length is 0.

Function No Overloading

ECMAScript functions cannot be overloaded in the traditional sense. The functions in ECMAScript don't have signatures because the arguments are represented as an array containing zero or more values. Without function signatures, true overloading is not possible. If two functions are defined to have the same name in ECMAScript, it is the last function that becomes the owner of that name. Consider the following example: function addSomeNumber(num) { return num + 100; } function addSomeNumber(num) { return num + 200; } let result = addSomeNumber(100); // 300 Here, the function addSomeNumber() is defined twice. The first version of the function adds 100 to the argument, and the second adds 200. When the last line is called, it returns 300 because the second function has overwritten the first. It's possible to simulate overloading of methods by checking the type and number of arguments that have been passed into a function and then reacting accordingly. In the previous example, declaring two functions with the same name always results in the last function overwriting the previous one. This code is almost exactly equivalent to the following: let addSomeNumber = function(num) { return num + 100; }; addSomeNumber = function(num) { return num + 200; }; let result = addSomeNumber(100); // 300 In this rewritten code, it's much easier to see exactly what is going on. The variable addSomeNumber is simply being overwritten when the second function is created.

Function Default Parameter Values

ECMAScript 6 supports explicitly defining values for parameters if they are not provided when the function is invoked. ES6 default parameters is done using the = operator directly inside the function signature: function sayHi(name = 'CSS') { return `Hi ${name} 3`; } console.log(sayHi('HTML')); // 'Hi HTML 3' console.log(sayHi()); // 'Hi CSS 3' Output: Passing undefined as an argument is treated the same as not passing any argument, which allows for multiple independent default variables: function sayHi(name = 'CSS', numerals = '3') { return `Hi ${name} ${numerals}`; } console.log(sayHi()); // 'Hi CSS 3' console.log(sayHi('HTML')); // 'Hi HTML 3' console.log(sayHi(undefined, '4')); // 'Hi CSS 4' When using default parameters, the arguments object's value does not reflect the default value of a parameter, but rather the argument passed to the function. This mirrors the ES5 strict mode behavior and is valuable because it preserves the values as they were passed when the function was invoked: function sayHi(name = 'CSS') { name = 'HTML'; return `Hi ${arguments[0]}`; } console.log(sayHi()); // 'Hi undefined' console.log(sayHi('HTML')); // 'Hi HTML' Default parameter values are not limited to primitives or object types, you can also calculate a value from an invoked function: let romanNumerals = ['I', 'II', 'III', 'IV', 'V', 'VI']; let ordinality = 0; function getNumerals() { // Increment the ordinality after using it to // index into the numerals array return romanNumerals[ordinality++]; } function sayHi(name = 'CSS', numerals = getNumerals()) { return `Hi ${name} ${numerals}`; } console.log(sayHi()); // 'Hi CSS I' console.log(sayHi('HTML', 'XVI')); // 'Hi HTML XVI' console.log(sayHi()); // 'Hi CSS II' console.log(sayHi()); // 'Hi CSS III' The function default parameter is only invoked when the function itself is invoked, not when the function is defined. The method that calculates the default value is only invoked when the argument is not provided. Arrow functions can use default parameters in the same way, although it means parentheses around a single argument is no longer optional if a default value is specified: let sayHi = (name = 'CSS') => `Hi ${name}`; console.log(sayHi()); // Hi CSS

Function Default Parameter Scope and Temporal Dead Zone

Because you can define objects and invoke functions on the fly when evaluating default parameter values. There is execution scope for function parameters. Defining multiple parameters with default values operates in effectively the same way as declaring variables sequentially using the let keyword. Consider the following function: function sayHi(name = 'CSS', numerals = '3') { return `Hi ${name} ${numerals}`; } console.log(sayHi()); // Hi CSS 3 The default parameter values are initialized in the order in which they are listed in the list of parameters. You can think of it as behaving similar to the following: function sayHi() { let name = 'CSS'; let numerals = '3'; return `Hi ${name} ${numerals}`; } Because the parameters are initialized in order, parameters that have their default value defined later can reference an earlier parameter. The following example does exactly this: function sayHi(name = 'CSS', numerals = name) { return `Hi ${name} ${numerals}`; } console.log(sayHi()); // Hi CSS CSS The order of parameter initialization follows the same Temporal Dead Zone rules specifying that parameter values cannot reference other parameter values that are defined later. This would throw an error: // Error function sayHi(name = numerals, numerals = '3') { return `Hi ${name} ${numerals}`; } Parameters also exist inside their own scope, and therefore cannot reference the scope of the function body. This would throw an error: // Error function sayHi(name = 'CSS', numerals = defaultNumeral) { let defaultNumeral = '3'; return `Hi ${name} ${numerals}`; }

Function Spread Arguments

ECMAScript 6 spread operator allows for a very elegant way of managing and grouping collections. The spread operator is useful both when invoking a function, as well as when defining a function's parameters.

Spread Arguments

Instead of passing an array as a single function argument, we can break apart an array of values and individually pass each value as a separate argument. Suppose you have the following function defined, which sums all the values passed as arguments: let values = [1, 2, 3, 4]; function getSum() { let sum = 0; for (let i = 0; i < arguments.length; ++i) { sum += arguments[i]; } return sum; } This function expects each of its arguments to be an individual number that will be iterated through to find the sum. In ECMAScript 6, we can perform this action using the spread operator. Applying the spread operator to an iterable object and passing that as a single argument to a function will break apart that iterable object of size N and pass it to the function as N separate arguments. With the spread operator, you can unpack the outer array into individual arguments directly inside the function invocation: console.log(getSum(...values)); // 10 Because the size of the array is known, there are no restrictions on other parameters appearing before or after the spread operator, including other spread operators: console.log(getSum(-1, ...values)); // 9 console.log(getSum(...values, 5)); // 15 console.log(getSum(-1, ...values, 5)); // 14 console.log(getSum(...values, ...[5,6,7])); // 28 The presence of the spread operator is unknown to the arguments object; it will treat the value being broken apart as separate pieces because that is how they are passed to the function: let values = [1,2,3,4] function countArguments() { console.log(arguments.length); } countArguments(-1, ...values); // 5 countArguments(...values, 5); // 5 countArguments(-1, ...values, 5); // 6 countArguments(...values, ...[5,6,7]); // 7 The arguments object is only one way to consume spread arguments. Spread arguments can be used as named parameters in both standard functions and arrow functions, as well as along side default arguments: function getProduct(a, b, c = 1) { return a * b * c; } let getSum = (a, b, c = 0) => { return a + b + c; } console.log(getProduct(...[1,2])); // 2 console.log(getProduct(...[1,2,3])); // 6 console.log(getProduct(...[1,2,3,4])); // 6 console.log(getSum(...[0,1])); // 1 console.log(getSum(...[0,1,2])); // 3 console.log(getSum(...[0,1,2,3])); // 3

Function Rest Parameter

When composing a function definition, instead of handling parameters individually, We can use the spread operator to combine ranges of parameters of variable length into a single array. The rest parameter becomes a formal Array object. function getSum(...values) { // Sequentially sum all elements in 'values' // Initial total = 0 return values.reduce((x, y) => x + y, 0); } console.log(getSum(1,2,3)); // 6 If there are named parameters preceding the rest parameter, it will assume the size of the remaining parameters that remain unnamed, or an empty array if there are none. Because the rest parameter is variable in size, you are only able to use it as the last formal parameter: // Error function getProduct(...values, lastValue) {} // OK function myFunc(firstValue, ...values) { console.log(values); } myFunc(); // [] myFunc(1); // [] myFunc(1,2); // [2] myFunc(1,2,3); // [2, 3] The arrow functions support rest parameters, which affords you behavior that is extremely similar to arguments: let getSum = (...values) => { return values.reduce((x, y) => x + y, 0); } console.log(getSum(1,2,3)); // 6 Using a rest parameter does not affect the arguments object-it will still exactly reflect what was passed to the function: function getSum(...values) { console.log(arguments.length); // 3 console.log(arguments); // [1, 2, 3] console.log(values); // [1, 2, 3] } console.log(getSum(1,2,3));

Function Declarations Versus Function Expressions

Function declarations are read and available in an execution context before any code is executed, whereas function expressions aren't complete until the execution reaches that line of code. Consider the following: // OK console.log(sum(10, 10)); function sum(num1, num2) { return num1 + num2; } This code runs perfectly because function declarations are read and added to the execution context before the code begins running through a process called function declaration hoisting. As the code is being evaluated, the JavaScript engine does a first pass for function declarations and pulls them to the top of the source tree. So even though the function declaration appears after its usage in the code, the engine changes this to hoist the function declarations to the top. Changing the function declaration to an equivalent function expression, as in the following example, will cause an error during execution: // Error console.log(sum(10, 10)); let sum = function(num1, num2) { return num1 + num2; }; This updated code will cause an error because the function is part of an initialization statement, not part of a function declaration. That means the function isn't available in the variable sum until the let statement has been executed. This is not a consequence of using let, as using the var keyword will result in the same problem: console.log(sum(10, 10));//error var sum = function(num1, num2) { return num1 + num2; }; Output: Aside from this difference in when the function is available by the given name, the two syntaxes are equivalent.

Function new.target object

Functions have always been able to behave as both a constructor to instantiate a new object, and as a normal callable function. ECMAScript 6 can determine if a function was invoked with the new keyword using new.target. If a function is called normally, new.target will be undefined. If a function is called using the new keyword, new.target will reference the constructor or function. function Person() { if (!new.target) { throw 'Person must be instantiated using "new"' } console.log('Person instantiated using "new"'; } new Person(); // Person instantiated using "new" Person(); // Error: Person must be instantiated using "new"

Functions As Values

Because function names in ECMAScript are variables, functions can be used anywhere variables can be used. We can pass a function into another function as an argument, and return a function as the result of another function. Consider the following function: function callSomeFunction(someFunction, someArgument) { return someFunction(someArgument); } This function accepts two arguments. The first argument should be a function, and the second argument should be a value to pass to that function. Any function can then be passed in as follows: function add10(num) { return num + 10; } let result1 = callSomeFunction(add10, 10); console.log(result1); // 20 function getGreeting(name) { return "Hello, " + name; } let result2 = callSomeFunction(getGreeting, "CSS"); console.log(result2); // "Hello, CSS" The callSomeFunction() function is generic, so it doesn't matter what function is passed in as the first argument-the result will always be returned from the first argument being executed.

Return a function from another function

In Javascript returning a function from a function is possible. For instance, suppose that you have an array of objects and want to sort the array on an arbitrary object property. A comparison function for the array's sort() method accepts only two arguments, which are the values to compare, but really you need a way to indicate which property to sort by. This problem can be addressed by defining a function to create a comparison function based on a property name, as in the following example: function createComparisonFunction(propertyName) { return function(object1, object2) { let value1 = object1[propertyName]; let value2 = object2[propertyName]; if (value1 < value2) { return -1; } else if (value1 > value2) { return 1; } else { return 0; } }; } This function is a function inside of a function, preceded by the return operator. The propertyName argument is accessible from the inner function and is used with bracket notation to retrieve the value of the given property. Once the property values are retrieved, a simple comparison can be done. This function can be used as in the following example: let data = [ {name: "HTML", age: 18}, {name: "CSS", age: 19} ]; data.sort(createComparisonFunction("name")); console.log(data[0].name); // CSS data.sort(createComparisonFunction("age")); console.log(data[0].name); // HTML In this code, an array called data is created with two objects. Each object has a name property and an age property. By default, the sort() method would call toString() on each object to determine the sort order, which wouldn't give logical results in this case. Calling createComparisonFunction("name") creates a comparison function that sorts based on the name property, which means the first item will have the name "CSS" and an age of 19. When createComparisonFunction("age") is called, it creates a comparison function that sorts based on the age property, meaning the first item will be the one with its name equal to "HTML" and age equal to 28.

Function this pointer

The function this pointer behaves differently when used inside a standard function and an arrow function. Inside a standard function, it is a reference to the context object that the function is operating on-often called the this value. When a function is called in the global scope of a web page, the this object points to window. Consider the following: window.color = 'red'; let o = { color: 'blue' }; function sayColor() { console.log(this.color); } sayColor(); // 'red' o.sayColor = sayColor; o.sayColor(); // 'blue' The function sayColor() is defined globally but references the this object. The value of this is not determined until the function is called, so its value may not be consistent throughout the code execution. When sayColor() is called in the global scope, it outputs "red" because this is pointing to window, which means this.color evaluates to window.color. By assigning the function to the object o and then calling o.sayColor(), the this object points to o, so this.color evaluates to o.color and "blue" is displayed.

Arrow function

Inside an arrow function, this references the context in which the arrow function expression is defined. This is demonstrated in the following example, where two different invocations of sayColor() both reference the property of the window object, which is the context inside which the arrow function was initially defined: window.color = 'red'; let o = { color: 'blue' }; let sayColor = () => console.log(this.color); sayColor(); // 'red' o.sayColor = sayColor; o.sayColor(); // 'red' This is especially useful in situations where events or timeouts will invoke a function inside a callback where the invoking object is not the intended object. When an arrow function is used in these situations, the context referenced by this is preserved: function Person() { this.myName = 'CSS'; // 'this' will be the Person instance setTimeout(() => console.log(this.myName), 1000); } function Friend() { this.myName = 'HTML'; // 'this' will be the window object setTimeout(function() { console.log(this.myName); }, 1000); } new Person(); // CSS new Friend(); // undefined Output: The function names are variables containing pointers, so the global sayColor() function and o.sayColor() point to the same function even though they execute in different contexts.

Function caller object

ECMAScript function object caller contains a reference to the function that called this function or null if the function was called from the global scope. For example: function outer() { inner(); } function inner() { console.log(inner.caller); } outer(); Output: This code displays an alert with the source text of the outer() function. Because outer() calls inner(), inner.caller points back to outer(). For looser coupling, you can also access the same information via arguments.callee.caller: function outer() { inner(); } function inner() { console.log(arguments.callee.caller); } outer(); Output: When function code executes in strict mode, attempting to access arguments.callee results in an error. ECMAScript 5 also defines arguments.caller, which also results in an error in strict mode and is always undefined outside of strict mode. In strict mode you cannot assign a value to the caller property of a function. Doing so results in an error.

Function apply() method

There are two additional methods for functions: apply() and call(). These methods both call the function with a specific this value, effectively setting the value of the this object inside the function body. The apply() method accepts two arguments: the value of this inside the function and an array of arguments. This second argument may be an instance of Array, but it can also be the arguments object. Consider the following: function sum(num1, num2) { return num1 + num2; } function callSum1(num1, num2) { return sum.apply(this, arguments); // passing in arguments object } function callSum2(num1, num2) { return sum.apply(this, [num1, num2]); // passing in array } console.log(callSum1(10, 10)); // 20 console.log(callSum2(10, 10)); // 20 In this example, callSum1() executes the sum() method, passing in this as the this value, which is equal to window because it's being called in the global scope, and also passing in the arguments object. The callSum2() method also calls sum(), but it passes in an array of the arguments instead. Both functions will execute and return the correct result. In strict mode, the this value of a function called without a context object is not coerced to window. Instead, this becomes undefined unless explicitly set by either attaching the function to an object or using apply() or call().

Function call() method

The call() method exhibits the same behavior as apply(), but arguments are passed to it differently. The first argument is the this value, but the remaining arguments are passed directly into the function. Using call() arguments must be enumerated specifically, as in this example: function sum(num1, num2) { return num1 + num2; } function callSum(num1, num2) { return sum.call(this, num1, num2); } console.log(callSum(10, 10)); // 20 The callSum() method must pass in each of its arguments explicitly into the call() method. The result is the same as using apply(). The decision to use either apply() or call() depends solely on the easiest way for you to pass arguments into the function. If you intend to pass in the arguments object directly or if you already have an array of data to pass in, then apply() is the better choice; otherwise, call() may be a more appropriate choice. If there are no arguments to pass in, these methods are identical.

Function augment this via apply() and call() method

The apply() and call() can augment the this value inside of the function. Consider the following example: window.color = 'red'; let o = { color: 'blue' }; function sayColor() { console.log(this.color); } sayColor(); // red sayColor.call(this); // red sayColor.call(window); // red sayColor.call(o); // blue sayColor() is defined as a global function, and when it's called in the global scope, it displays "red" because this.color evaluates to window.color. You can then call the function explicitly in the global scope by using sayColor.call(this) and sayColor.call(window), which both display "red". Running sayColor.call(o) switches the context of the function such that this points to o, resulting in a display of "blue". The advantage of using call() or apply() to augment the scope is that the object doesn't need to know anything about the method. In the first version of this example, the sayColor() function was placed directly on the object o before it was called; in the updated example, that step is no longer necessary.

Function bind() method

ECMAScript 5 defines an additional method called bind(). The bind() method creates a new function instance whose this value is bound to the value that was passed into bind(). For example: window.color = 'red'; var o = { color: 'blue' }; function sayColor() { console.log(this.color); } let objectSayColor = sayColor.bind(o); objectSayColor(); // blue Here, a new function called objectSayColor() is created from sayColor() by calling bind() and passing in the object o. The objectSayColor() function has a this value equivalent to o, so calling the function, even as a global call, results in the string "blue" being displayed. For functions, the inherited methods toLocaleString() and toString() always return the function's code.

Function Expressions

There are two ways to define a function: by function declaration and by function expression. The first, function declaration, has the following form: function functionName(arg0, arg1, arg2) { // function body } One of the key characteristics of function declarations is function declaration hoisting, whereby function declarations are read before the code executes. That means a function declaration may appear after code that calls it and still work: sayHi(); function sayHi() { console.log("Hi!"); } This example doesn't throw an error because the function declaration is read first before the code begins to execute.

Function expression

The second way to create a function is by using a function expression. Function expressions have several forms. The most common is as follows: let functionName = function(arg0, arg1, arg2) { // function body }; This pattern of function expression looks like a normal variable assignment. A function is created and assigned to the variable functionName. The created function is considered to be an anonymous function, because it has no identifier after the function keyword. Anonymous functions are also sometimes called lambda functions. This means the name property is the empty string. Function expressions act like other expressions and, therefore, must be assigned before usage. The following causes an error: sayHi(); // Error! function doesn't exist yet let sayHi = function() { console.log("Hi!"); };

Function hoisting

Understanding function hoisting is key to understanding the differences between function declarations and function expressions. We can use function expressions in this way: // OK let sayHi; if (condition) { sayHi = function() { console.log("Hi!"); }; } else { sayHi = function() { console.log("Yo!"); }; } This example behaves the way you would expect, assigning the correct function expression to the variable sayHi based on condition. The ability to create functions for assignment to variables also allows you to return functions as the value of other functions. function createComparisonFunction(propertyName) { return function(object1, object2) { let value1 = object1[propertyName]; let value2 = object2[propertyName]; if (value1 < value2) { return -1; } else if (value1 > value2) { return 1; } else { return 0; } }; } createComparisonFunction() returns an anonymous function. The returned function will, presumably, be either assigned to a variable or otherwise called, but within createComparisonFunction() it is anonymous. Any time a function is being used as a value, it is a function expression.

Function Recursion

A recursive function is formed when a function calls itself, as in the following example: function factorial(num) { if (num <= 1) { return 1; } else { return num * factorial(num - 1); } } This is the classic recursive factorial function. The arguments.callee is a pointer to the function being executed and, as such, can be used to call the function recursively, as shown here: function factorial(num) { if (num <= 1) { return 1; } else { return num * arguments.callee(num - 1); } } We should use arguments.callee of the function name whenever you're writing recursive functions. The value of arguments.callee is not accessible to a script running in strict mode and will cause an error when attempts are made to read it. Instead, you can use named function expressions to achieve the same result. For example: const factorial = (function f(num) { if (num <= 1) { return 1; } else { return num * f(num - 1); } }); In this code, a named function expression f() is created and assigned to the variable factorial. The name f remains the same even if the function is assigned to another variable, so the recursive call will always execute correctly. This pattern works in both non strict mode and strict mode.

Function Closures

Closures are functions that have access to variables from another function's scope. This is often accomplished by creating a function inside a function, as in the following example: function createComparisonFunction(propertyName) { return function(object1, object2) { let value1 = object1[propertyName]; let value2 = object2[propertyName]; if (value1 < value2) { return -1; } else if (value1 > value2) { return 1; } else { return 0; } }; } The inner anonymous function is accessing a variable propertyName from the outer function. Even after the inner function has been returned and is being used elsewhere, it has access to that variable. This occurs because the inner function's scope chain includes the scope of createComparisonFunction(). When a function is called, an execution context is created, and its scope chain is created. The activation object for the function is initialized with values for arguments and any named arguments. The outer function's activation object is the second object in the scope chain. This process continues for all containing functions until the scope chain terminates with the global execution context. As the function executes, variables are looked up in the scope chain for the reading and writing of values. Consider the following: function compare(value1, value2) { if (value1 < value2) { return -1; } else if (value1 > value2) { return 1; } else { return 0; } } let result = compare(5, 10); This code defines a function named compare() that is called in the global execution context. When compare() is called for the first time, a new activation object is created that contains arguments, value1, and value2. The global execution context's variable object is next in the compare() execution context's scope chain, which contains this, result, and compare. Behind the scenes, an object represents the variables in each execution context. The global context's variable object always exists, whereas local context variable objects, such as the one for compare(), exist only while the function is being executed. In this example, that means the compare() function's execution context has two variable objects in its scope chain: the local activation object and the global variable object.

Immediately Invoked Function Expressions IIFE

An anonymous function that is called immediately is most often called an immediately invoked function expression (IIFE). It resembles a function declaration, but because it is enclosed in parentheses it is interpreted as a function expression. This function is then called via the second set of parentheses at the end. The basic syntax is as follows: (function() { // block code here })(); The use of an IIFE to simulate block scope uses values defined inside a function expression that is executed immediately, thereby offering a block scope-like behavior using function scoped variables. The utility of the IIFE was much greater in previous versions of ECMAScript 6 where block scoped variables were not supported. Consider the following example: // IIFE (function () { for (var i = 0; i < count; i++) { console.log(i); } })(); console.log(i); // Throws an error The preceding code will error when the console.log() outside the IIFE attempts to execute because the variable defined inside the IIFE is not scoped to be accessible from outside the function expression. With ECMAScript 6, the IIFE is no longer required for emulating block scope, as block scoped variables will offer the exact same behavior without the need for an IIFE: Inline block scope { let i; for (i = 0; i < count; i++) { console.log(i); } } console.log(i); // Throws an error Function block scope for (let i = 0; i < count; i++) { console.log(i); } console.log(i); // Throws an error

Function Private Variables

Strictly speaking, JavaScript has no concept of private members; all object properties are public. There is a concept of private variables. Any variable defined inside a function or block is considered private because it is inaccessible outside that function. This includes function arguments, local variables, and functions defined inside other functions. Consider the following: function add(num1, num2) { let sum = num1 + num2; return sum; } In this function, there are three private variables: num1, num2, and sum. These variables are accessible inside the function but can't be accessed outside it. If a closure were to be created inside this function, it would have access to these variables through its scope chain. Using this knowledge, you can create public methods that have access to private variables. A privileged method is a public method that has access to private variables and/or private functions. There are two ways to create privileged methods on objects. The first is to do so inside a constructor, as in this example: function MyObject() { // private variables and functions let privateVariable = 10; function privateFunction() { return false; } // privileged methods this.publicMethod = function() { privateVariable++; return privateFunction(); }; } This pattern defines all private variables and functions inside the constructor. Then privileged methods can be created to access those private members. This works because the privileged methods, when defined in the constructor, become closures with full access to all variables and functions defined inside the constructor's scope. In this example, the variable privateVariable and the function privateFunction() are accessed only by publicMethod(). Once an instance of MyObject is created, there is no way to access privateVariable and privateFunction() directly; you can do so only by way of publicMethod(). You can define private and privileged members to hide data that should not be changed directly, as in this example: function Person(name) { this.getName = function() { return name; }; this.setName = function (value) { name = value; }; } let person = new Person('CSS'); console.log(person.getName()); // 'CSS' person.setName('HTML'); console.log(person.getName()); // 'HTML' The constructor in this code defines two privileged methods: getName() and setName(). Each method is accessible outside the constructor and accesses the private name variable. Outside the Person constructor, there is no way to access name. Because both methods are defined inside the constructor, they are closures and have access to name through the scope chain. The private variable name is unique to each instance of Person because the methods are being re-created each time the constructor is called.

Note

You must use the constructor pattern to accomplish this result. The constructor pattern has issues since new methods are created for each instance. Using static private variables to achieve privileged methods enables the user to avoid this problem.

Function Static Private Variables

We can create methods which can access private variables by using a private scope to define the private variables or functions. The pattern is as follows: (function() { // private variables and functions let privateVariable = 10; function privateFunction() { return false; } // constructor MyObject = function() {}; // public and privileged methods MyObject.prototype.publicMethod = function() { privateVariable++; return privateFunction(); }; })(); In this pattern, a private scope is created to enclose the constructor and its methods. The private variables and functions are defined first, followed by the constructor and the public methods. Public methods are defined on the prototype, as in the typical prototype pattern. This pattern defines the constructor not by using a function declaration but instead by using a function expression. Function declarations always create local functions, which is undesirable in this case. A variable declaration keyword is not used with MyObject. Initializing an undeclared variable always creates a global variable, so MyObject becomes global and available outside the private scope. Assigning to an undeclared variable in strict mode causes an error. The privileged method, being a closure, always holds a reference to the containing scope. Consider the following: (function() {/* w w w. d e m o 2 s . c o m */ let name = ''; Person = function(value) { name = value; }; Person.prototype.getName = function() { return name; }; Person.prototype.setName = function(value) { name = value; }; })(); let person1 = new Person('CSS'); console.log(person1.getName()); // 'CSS' person1.setName('HTML'); console.log(person1.getName()); // 'HTML' let person2 = new Person('Michael'); console.log(person1.getName()); // 'Michael' console.log(person2.getName()); // 'Michael' The Person constructor in this example has access to the private variable name, as do the getName() and setName() methods. Using this pattern, the name variable becomes static and will be used among all instances. This means calling setName() on one instance affects all other instances. Calling setName() or creating a new Person instance sets the name variable to a new value. This causes all instances to return the same value.

Function Module Pattern for Singleton

The module pattern, as described by Douglas Crockford, does the same for singletons. Singletons are objects of which there will only ever be one instance. Traditionally, singletons are created in JavaScript using object literal notation, as shown in the following example: let singleton = { name: value, method() { // method code here } }; The module pattern augments the basic singleton to allow for private variables and privileged methods, taking the following format: let singleton = function() { // private variables and functions let privateVariable = 10; function privateFunction() { return false; } // privileged/public methods and properties return { publicProperty: true, publicMethod() { privateVariable++; return privateFunction(); } }; }(); The module pattern uses an anonymous function that returns an object. Inside of the anonymous function, the private variables and functions are defined first. After that, an object literal is returned as the function value. That object literal contains only properties and methods that should be public. Because the object is defined inside the anonymous function, all of the public methods have access to the private variables and functions. Essentially, the object literal defines the public interface for the singleton. This can be useful when the singleton requires some sort of initialization and access to private variables, as in this example: let application = function() { // private variables and functions let components = new Array(); // initialization components.push(new BaseComponent());// ww w . de m o 2 s . c o m // public interface return { getComponentCount() { return components.length; }, registerComponent(component) { if (typeof component == 'object') { components.push(component); } } }; }(); The getComponentCount() and registerComponent() methods are privileged methods with access to the components array. The former simply returns the number of registered components, and the latter registers a new component. The module pattern is useful for cases such as when a single object is created, initialized with some data, and then needs to expose public methods that have access to private data. Every singleton created in this manner is an instance of Object because ultimately an object literal represents it.

Function Module-Augmentation Pattern

The module augmentation pattern is useful when the singleton object needs to be an instance of a particular type but must be augmented with additional properties and/or methods. Consider the following example: let singleton = function() { // private variables and functions let privateVariable = 10; function privateFunction() { return false; }/* w w w . d e m o2 s . c o m*/ // create object let object = new CustomType(); // add privileged/public properties and methods object.publicProperty = true; object.publicMethod = function() { privateVariable++; return privateFunction(); }; // return the object return object; }(); If the application object in the module pattern example had to be an instance of BaseComponent, the following code could be used: let application = function() { // private variables and functions let components = new Array(); // initialization components.push(new BaseComponent());// w w w .d e m o 2 s . c o m // create a local copy of application let app = new BaseComponent(); // public interface app.getComponentCount = function() { return components.length; }; app.registerComponent = function(component) { if (typeof component == "object") { components.push(component); } }; // return it return app; }(); In the application singleton, the private variables are defined first, as in the previous example. The main difference is the creation of a variable named app that is a new instance of BaseComponent. This is the local version of what will become the application object. Public methods are then added onto the app object to access the private variables. The last step is to return the app object, which assigns it to application.

Basic Reference Types

A reference value, or object, is an instance of a specific reference type. In ECMAScript, reference types are structures used to group data and functionality together. They should not be called classes. ECMAScript lacks some basic constructs that have traditionally been associated with object-oriented programming, including classes and interfaces. Reference types are sometimes called object definitions because they describe the properties and methods that objects should have. Even though reference types are similar to classes, the two concepts are not equivalent. ECMAScript objects are considered to be instances of a particular reference type. New objects are created by using the new operator followed by a constructor. A constructor is simply a function whose purpose is to create a new object. Consider the following line of code: let now = new Date(); This code creates a new instance of the Date reference type and stores it in the variable now. The constructor being used is Date(), which creates a simple object with only the default properties and methods. ECMAScript provides a number of native reference types, such as Date, to help developers with common computing tasks.

Date Type

The ECMAScript Date type stores dates as the number of milliseconds that have passed since midnight on January 1, 1970 UTC (Universal Time Code). To create a date object, use the new operator along with the Date constructor, like this: let now = new Date(); When the Date constructor is used without any arguments, the created object is assigned the current date and time. To create a date based on another date or time, you must pass in the millisecond representation of the date, the number of milliseconds after midnight, January 1, 1970 UTC, the Unix epoch. To aid in this process, ECMAScript provides two methods: Date.parse() and Date.UTC(). The Date.parse() method accepts a string argument representing a date. It attempts to convert the string into a millisecond representation of a date. The following date formats are supported:
Format Example
month/date/year 5/24/2020
month_name date, year May 24, 2020
day_of_week month_name date year hours:minutes:seconds time_zoneTue May 23 2020 00:00:00 GMT-0700
ISO 8601 extended format YYYY-MM-DDTHH:mm:ss.sssZ 2020-05-23T00:00:00
For instance, to create a date object for May 23, 2020, you can use the following code: let someDate = new Date(Date.parse("May 23, 2020")); If the string passed into Date.parse() doesn't represent a date, then it returns NaN. The Date constructor will call Date.parse() behind the scenes if a string is passed in directly, meaning that the following code is identical to the previous example: let someDate = new Date("May 23, 2020"); This code produces the same result as the previous example. The Date.UTC() method returns the millisecond representation of a date but constructs that value using different information than Date.parse(). The arguments for Date.UTC() are the year, the zero-based month (January is 0, February is 1, and so on), the day of the month (1 through 31), and the hours (0 through 23), minutes, seconds, and milliseconds of the time. Of these arguments, only the first two (year and month) are required. If the day of the month isn't supplied, it's assumed to be 1, while all other omitted arguments are assumed to be 0. Here are two examples of Date.UTC() in action: // January 1, 2000 at midnight GMT let y2k = new Date(Date.UTC(2000, 0)); // May 5, 2020 at 5:55:55 PM GMT let allFives = new Date(Date.UTC(2020, 4, 5, 17, 55, 55)); Two dates are created in this example. The first date is for midnight (GMT) on January 1, 2000, which is represented by the year 2000 and the month 0 (which is January). Because the other arguments are filled in (the day of the month as 1 and everything else as 0), the result is the first day of the month at midnight. The second date represents May 5, 2020, at 5:55:55 PM GMT. Date.UTC() is mimicked by the Date constructor but with one major difference: the date and time created are in the local time zone, not in GMT. However, the Date constructor takes the same arguments as Date.UTC(), so if the first argument is a number, the constructor assumes that it is the year of a date, the second argument is the month, and so on. The preceding example can then be rewritten as this: // January 1, 2000 at midnight in local time let y2k = new Date(2000, 0); // May 5, 2020 at 5:55:55 PM local time let allFives = new Date(2020, 4, 5, 17, 55, 55); This code creates the same two dates as the previous example, but this time both dates are in the local time zone as determined by the system settings. ECMAScript offers Date.now(), which returns the millisecond representation of the date and time at which the method is executed. This method can be used for code profiling, such as: // get start time let start = Date.now(); // call a function doSomething(); // get stop time let stop = Date.now(), result = stop - start;

Date toString and valueOf() method

The Date type overrides toLocaleString(), toString(), and valueOf(). The Date type's toLocaleString() method returns the date and time in a format appropriate for the locale in which the browser is being run. This often means that the format includes AM or PM for the time and doesn't include any time-zone information. The toString() method typically returns the date and time with time-zone information, and the time is typically indicated in 24-hour notation (hours ranging from 0 to 23). The valueOf() method for the Date type returns the milliseconds representation of the date so that operators (such as less-than and greater than) will work appropriately for date values. Consider this example: let date1 = new Date(2019, 0, 1); // "January 1, 2019" let date2 = new Date(2019, 1, 1); // "February 1, 2019" console.log(date1 < date2); // true console.log(date1 > date2); // false The date January 1, 2019, comes before February 1, 2019, so it would make sense to say that the former is less than the latter. Because the milliseconds representation of January 1, 2019, is less than that of February 1, 2019, the less-than operator returns true when the dates are compared, providing an easy way to determine the order of dates.

Date-Formatting Methods

There are several Date type methods used specifically to format the date as a string. They are as follows:
Method Description
toDateString()
toTimeString()
toLocaleDateString()
toLocaleTimeString()
toUTCString()
Displays the date's day of the week, month, day of the month, and year in an implementation-specific format.
Displays the date's hours, minutes, seconds, and time zone in an implementation-specific format.
Displays the date's day of the week, month, day of the month, and year in an implementation and locale-specific format.
Displays the date's hours, minutes, and seconds in an implementation-specific format.
Displays the complete UTC date in an implementation-specific format.
The output of these methods varies widely from browser to browser and therefore can't be employed in a user interface for consistent display of a date. There is a method called toGMTString(), which is equivalent to toUTCString() and is provided for backwards compatibility. const d = new Date(); console.log(d.toDateString()); console.log(d.toTimeString()); console.log(d.toLocaleDateString()); console.log(d.toLocaleTimeString()); console.log(d.toUTCString()); Output:

Date/Time Component Methods

The Date type can deal directly with getting and setting specific parts of the date value.
Method Description
getTime() Returns the milliseconds representation of the date; same as valueOf().
setTime(milliseconds) Sets the milliseconds representation of the date, thus changing the entire date.
getFullYear() Returns the four-digit year (2019 instead of just 19).
getUTCFullYear() Returns the four-digit year of the UTC date value.
setFullYear(year) Sets the year of the date.
The year must be given with four digits (2019 instead of just 19).
setUTCFullYear(year) Sets the year of the UTC date.
The year must be given with four digits (2019 instead of just 19).
getMonth() Returns the month of the date, where 0 represents January and 11 represents December.
getUTCMonth() Returns the month of the UTC date, where 0 represents January and 11 represents December.
setMonth(month) Sets the month of the date, which is any number 0 or greater.
Numbers greater than 11 add years.
setUTCMonth(month) Sets the month of the UTC date, which is any number 0 or greater.
Numbers greater than 11 add years.
getDate() Returns the day of the month (1 through 31) for the date.
getUTCDate() Returns the day of the month (1 through 31) for the UTC date.
setDate(date) Sets the day of the month for the date.
If the date is greater than the number of days in the month, the month value also gets increased.
setUTCDate(date) Sets the day of the month for the UTC date.
If the date is greater than the number of days in the month, the month value also gets increased.
getDay() Returns the date's day of the week as a number (where 0 represents Sunday and 6 represents Saturday).
getUTCDay() Returns the UTC date's day of the week as a number (where 0 represents Sunday and 6 represents Saturday).
getHours() Returns the date's hours as a number between 0 and 23.
getUTCHours() Returns the UTC date's hours as a number between 0 and 23.
setHours (hours) Sets the date's hours.
Setting the hours to a number greater than 23 also increments the day of the month.
setUTCHours(hours) Sets the UTC date's hours.
Setting the hours to a number greater than 23 also increments the day of the month.
getMinutes() Returns the date's minutes as a number between 0 and 59.
getUTCMinutes() Returns the UTC date's minutes as a number between 0 and 59.
setMinutes(minutes) Sets the date's minutes.
Setting the minutes to a number greater than 59 also increments the hour.
setUTCMinutes(minutes) Sets the UTC date's minutes.
Setting the minutes to a number greater than 59 also increments the hour.
getSeconds() Returns the date's seconds as a number between 0 and 59.
getUTCSeconds() Returns the UTC date's seconds as a number between 0 and 59.
setSeconds(seconds) Sets the date's seconds.
Setting the seconds to a number greater than 59 also increments the minutes.
setUTCSeconds(seconds) Sets the UTC date's seconds.
Setting the seconds to a number greater than 59 also increments the minutes.
getMilliseconds() Returns the date's milliseconds.
getUTCMilliseconds() Returns the UTC date's milliseconds.
setMilliseconds(milliseconds) Sets the date's milliseconds.
setUTCMilliseconds
(milliseconds)
Sets the UTC date's milliseconds.
getTimezoneOffset() Returns the number of minutes that the local time zone is offset from UTC.
For example, Eastern Standard Time returns 300.
This value changes when an area goes into Daylight Saving Time.

Primitive Wrapper Types, Number, String and Boolean

Three special reference types are designed to ease interaction with primitive values: the Boolean type, the Number type, and the String type. These types can act like the other reference types and they also have a special behavior related to their primitive-type equivalents. Every time a primitive value is read, an object of the corresponding primitive wrapper type is created behind the scenes, allowing access to any number of methods for manipulating the data. Consider the following example: let s1 = "some text"; let s2 = s1.substring(2); In this code, s1 is a variable containing a string, which is a primitive value. On the next line, the substring() method is called on s1 and stored in s2. Any time a string value is accessed in read mode, the following three steps occur: Create an instance of the String type. Call the specified method on the instance. Destroy the instance. You can think of these three steps as they're used in the following three lines of ECMAScript code: let s1 = new String("some text"); let s2 = s1.substring(2); s1 = null; This behavior allows the primitive string value to act like an object. These same three steps are repeated for Boolean and numeric values using the Boolean and Number types, respectively. For primitive wrapper types we cannot add new properties. Take this for example: let s1 = "some text"; s1.color = "red"; console.log(s1.color); // undefined Here, the second line attempts to add a color property to the string s1. However, when s1 is accessed on the third line, the color property is gone. This happens because the String object that was created in the second line is destroyed by the time the third line is executed. The third line creates its own String object, which doesn't have the color property.

Constructor

We can create the primitive wrapper objects explicitly using the Boolean, Number, and String constructors. This should not be done because it is confusing for developers as to whether they are dealing with a primitive or reference value. Calling typeof on an instance of a primitive wrapper type returns "object", and all primitive wrapper objects convert to the Boolean value true.

Factory method

The Object constructor also acts as a factory method and is capable of returning an instance of a primitive wrapper based on the type of value passed into the constructor. For example: let obj = new Object("some text"); console.log(obj instanceof String); // true When a string is passed into the Object constructor, an instance of String is created; a number argument results in an instance of Number, while a Boolean argument returns an instance of Boolean.

Without new

Calling a primitive wrapper constructor using new is not the same as calling the casting function of the same name. For example: let value = "25"; let number = Number(value); // casting function console.log(typeof number); // "number" let obj = new Number(value); // constructor console.log(typeof obj); // "object" In this example, the variable number is filled with a primitive number value of 25 while the variable obj is filled with an instance of Number.

Boolean Reference Type

The Boolean type is the reference type to the Boolean values. To create a Boolean object, use the Boolean constructor and pass in either true or false, as in the following example: let booleanObject = new Boolean(true); Instances of Boolean override the valueOf() method to return a primitive value of either true or false. The toString() method is also overridden to return a string of "true" or "false" when called.

Do not use it

We should not use Boolean objects in ECMAScript, since they can be rather confusing. The problem occurs when trying to use Boolean objects in Boolean expressions, as in this example: let falseObject = new Boolean(false); let result = falseObject && true; console.log(result); // true let falseValue = false; result = falseValue && true; console.log(result); // false In this code, a Boolean object is created with a value of false. That same object is then ANDed with the primitive value true. In Boolean math, false AND true equals false. However, in this line of code, it is the object named falseObject being evaluated, not its value (false). All objects are automatically converted to true in Boolean expressions, so falseObject actually is given a value of true in the expression. Then, true ANDed with true is equal to true. The typeof operator returns "boolean" for the primitive but "object" for the reference. A Boolean object is an instance of the Boolean type and will return true when used with the instanceof operator, whereas a primitive value returns false, as shown here: console.log(typeof falseObject); // object console.log(typeof falseValue); // boolean console.log(falseObject instanceof Boolean); // true console.log(falseValue instanceof Boolean); // false We need to understand the difference between a primitive Boolean value and a Boolean object-prefer to never use the latter.

Number Reference Type

The Number type is the reference type for numeric values. To create a Number object, use the Number constructor and pass in any number. Here's an example: let numberObject = new Number(10); The Number type overrides valueOf(), toLocaleString(), and toString(). The valueOf() method returns the primitive numeric value represented by the object, whereas the other two methods return the number as a string. The toString() method optionally accepts a single argument indicating the radix in which to represent the number, as shown in the following examples: let num = 10; console.log(num.toString()); // "10" console.log(num.toString(2)); // "1010" console.log(num.toString(8)); // "12" console.log(num.toString(10)); // "10" console.log(num.toString(16)); // "a" The Number type has several additional methods used to format numbers as strings.

toFixed()

The toFixed() method returns a string representation of a number with a specified number of decimal points, as in this example: let num = 10; console.log(num.toFixed(2)); // "10.00" Here, the toFixed() method is given an argument of 2, which indicates how many decimal places should be displayed. As a result, the method returns the string "10.00", filling out the empty decimal places with zeros. If the number has more than the given number of decimal places, the result is rounded to the nearest decimal place, as shown here: let num = 10.005; console.log(num.toFixed(2)); // "10.01" The rounding nature of toFixed() may be useful for applications dealing with currency. The toFixed() method can represent numbers with 0 through 20 decimal places.

toExponential()

The toExponential() method returns a string with the number formatted in exponential notation (aka e-notation). toExponential() accepts one argument, which is the number of decimal places to output. Consider this example: let num = 10; console.log(num.toExponential(1)); // "1.0e+1" This code outputs "1.0e+1" as the result.

toPrecision()

To have the most appropriate form of the number, use the toPrecision() method. The toPrecision() method returns either the fixed or the exponential representation of a number, depending on which makes the most sense. This method takes one argument, which is the total number of digits to use to represent the number (not including exponents). Here's an example: let num = 99; console.log(num.toPrecision(1)); // "1e+2" console.log(num.toPrecision(2)); // "99" console.log(num.toPrecision(3)); // "99.0" In this example, the first task is to represent the number 99 with a single digit, which results in "1e+2", otherwise known as 100. Because 99 cannot accurately be represented by just one digit, the method rounded up to 100, which can be represented using just one digit. Representing 99 with two digits yields "99" and with three digits returns "99.0". The toPrecision() method essentially determines whether to call toFixed() or toExponential() based on the numeric value you're working with; all three methods round up or down to accurately represent a number with the correct number of decimal places. The toPrecision() method can represent numbers with 1 through 21 decimal places.

Do not instantiate it

The Number object gives important functionality to numeric values but really should not be instantiated directly because of the same potential problems. The typeof and instanceof operators work differently when dealing with primitive numbers versus reference numbers, as shown in the following examples: let numberObject = new Number(10); let numberValue = 10; console.log(typeof numberObject); // "object" console.log(typeof numberValue); // "number" console.log(numberObject instanceof Number); // true console.log(numberValue instanceof Number); // false Primitive numbers always return "number" when typeof is called on them, whereas Number objects return "object". A Number object is an instance of Number, but a primitive number is not.

Use Number isInteger() to check if a value is an integer

ES6 Number.isInteger() method can test whether or not a number value is stored as an integer or not. This is useful when a trailing decimal 0 may mask whether or not the number is actually stored in floating point format: console.log(Number.isInteger(1)); // true console.log(Number.isInteger(1.00)); // true console.log(Number.isInteger(1.01)); // false Output:

Use Number isSafeInteger() to check Safe Integers

The IEEE 754 number format has a numerical range inside which a binary value can represent exactly one integer value. This numerical range extends from Number.MIN_SAFE_INTEGER, or -2^53 + 1, to Number.MAX_SAFE_INTEGER, or 2^53 - 1. Outside this range, you may attempt to store an integer, but the IEEE 754 encoding format means that this binary value may also alias to a completely different number. To tell if an integer is inside this range, the Number.isSafeInteger() method allows you to check this: console.log(Number.isSafeInteger(-1 * (2 ** 53))); // false console.log(Number.isSafeInteger(-1 * (2 ** 53) + 1)); // true console.log(Number.isSafeInteger(2 ** 53)); // false console.log(Number.isSafeInteger((2 ** 53) - 1)); // true Output:

Regexp Type

ECMAScript supports regular expressions through the RegExp type. Regular expressions are easy to create using syntax similar to Perl, as shown here: let expression = /pattern/flags; The pattern part of the expression can be any simple or regular expression, including character classes, quantifiers, grouping, look ahead, and back references. Each expression can have zero or more flags indicating how the expression should behave. Three supported flags represent matching modes, as follows:
Flag Description
g Indicates global mode, meaning the pattern will be applied to all of the string instead of stopping after the first match is found.
i Indicates case-insensitive mode, meaning the case of the pattern and the string are ignored when determining matches.
m Indicates multiline mode, meaning the pattern will continue looking for matches after reaching the end of one line of text.
y Indicates sticky mode, meaning the pattern will only look at the string contents beginning at lastIndex.
u Indicates Unicode mode is enabled.
A regular expression is created using a combination of a pattern and these flags to produce different results. Match all instances of "at" in a string. Match the first instance of "bat" or "cat", regardless of case. Match all three-character combinations ending with "at", regardless of case. All metacharacters must be escaped when used as part of the pattern. The metacharacters are as follows: ( [ { \ ^ $ | ) ] } ? * + . Each metacharacter has one or more uses in regular-expression syntax and so must be escaped by a backslash when you want to match the character in a string. Match the first instance of "bat" or "cat", regardless of case. Match the first instance of "[bc]at", regardless of case. Match all three-character combinations ending with "at", regardless of case. Match all instances of ".at", regardless of case. pattern1 matches all instances of "bat" or "cat", regardless of case. To match "[bc]at" directly, both square brackets need to be escaped with a backslash, as in pattern2. In pattern3, the dot indicates that any character can precede "at" to be a match. If you want to match ".at", then the dot needs to be escaped, as in pattern4. Regular expressions can also be created by using the RegExp constructor, which accepts two arguments: a string pattern to match and an optional string of flags to apply. Any regular expression that can be defined using literal syntax can also be defined using the constructor. Match the first instance of "bat" or "cat", regardless of case. Same as pattern1, just using the constructor. Here, pattern1 and pattern2 define equivalent regular expressions. Both arguments of the RegExp constructor are strings. Because the pattern argument of the RegExp constructor is a string, there are some instances in which you need to double-escape characters.

Regexp Type Escape

All metacharacters must be double-escaped, as must characters that are already escaped, such as \n. The \ character, which is normally escaped in strings as \\ becomes \\\\ when used in a regular-expression string. The following table shows some patterns in their literal form and the equivalent string that would be necessary to use the RegExp constructor.
Literal Pattern String Equivalent
/\[bc\]at/ "\\[bc\\]at"
/\.at/ "\\.at"
/name\/age/ "name\\/age"
/\d.\d{1,2}/ "\\d.\\d{1,2}"
/\w\\hello\\123/ "\\w\\\\hello\\\\123"

Regexp Type Constructor vs Literal

Creating a regular expression using a literal is not exactly the same as creating a regular expression using the RegExp constructor. Regular-expression literals always share the same RegExp instance, while creating a new RegExp via constructor always results in a new instance. Consider the following: let re = null; for (let i = 0; i < 10; i++) { re = /cat/g; re.test("cat dog cat"); } for (let i = 0; i < 10; i++) { re = new RegExp("cat", "g"); re.test("cat dog cat"); } In the first loop, there is only one instance of RegExp created for /cat/, even though it is specified in the body of the loop. Instance properties are not reset, so calling test() fails every other time through the loop. This happens because the "cat" is found in the first call to test(), but the second call begins its search from index 3, which is the end of the last match, and can't find it. Because the end of the string is found, the subsequent call to test() starts at the beginning again. The second loop uses the RegExp constructor to create the regular expression each time through the loop. Each call to test() returns true because a new instance of RegExp is created for each iteration. ECMAScript regular-expression literals create new instances of RegExp as if the RegExp constructor were called directly. We can copy existing regular expression instances and optionally modify their flags using the constructor: const re1 = /cat/g; console.log(re1); // "/cat/g" const re2 = new RegExp(re1); console.log(re2); // "/cat/g" const re3 = new RegExp(re1, "i"); console.log(re3); // "/cat/i"

RegExp Instance Properties

Each instance of RegExp has the following properties that allow you to get information about the pattern:
Property Type Description
global Boolean indicating whether the g flag has been set.
ignoreCase Boolean indicating whether the i flag has been set.
unicode Boolean indicating whether the u flag has been set.
sticky Boolean indicating whether the y flag has been set.
lastIndex integer indicating the character position where the next match will be attempted in the source string.
This value always begins as 0.
multiline Boolean indicating whether the m flag has been set.
source string source of the regular expression.
This is always returned as if specified in literal form (without opening and closing slashes) rather than a string pattern as passed into the constructor.
flags string flags of the regular expression.
This is always returned as if specified in literal form (without opening and closing slashes) rather than a string pattern as passed into the constructor.
These properties are helpful in identifying aspects of a regular expression. Here's an example: let pattern1 = /\[bc\]at/i; console.log(pattern1.global); // false console.log(pattern1.ignoreCase); // true console.log(pattern1.multiline); // false console.log(pattern1.lastIndex); // 0 console.log(pattern1.source); // "\[bc\]at" console.log(pattern1.flags); // "i" let pattern2 = new RegExp("\\[bc\\]at", "i"); console.log(pattern2.global); // false console.log(pattern2.ignoreCase); // true console.log(pattern2.multiline); // false console.log(pattern2.lastIndex); // 0 console.log(pattern2.source); // "\[bc\]at" console.log(pattern2.flags); // "i"

RegExp Instance Methods exec()

The primary method of a RegExp object is exec(), which is intended for use with capturing groups. This method accepts a single argument, which is the string on which to apply the pattern, and returns an array of information about the first match or null if no match was found. The returned array contains two additional properties: index, which is the location in the string where the pattern was matched, and input, which is the string that the expression was run against. In the array, the first item is the string that matches the entire pattern. Any additional items represent captured groups inside the expression. If there are no capturing groups in the pattern, then the array has only one item. Consider the following: let text = "cat dog and date and test"; let pattern = /dog( and date( and test)?)?/gi; let matches = pattern.exec(text); console.log(matches.index); console.log(matches.input); console.log(matches[0]); console.log(matches[1]); console.log(matches[2]); Output: In this example, the pattern has two capturing groups. When exec() is called on the string, a match is found. The exec() method returns information about one match at a time even if the pattern is global. When the global flag is not specified, calling exec() on the same string multiple times will always return information about the first match. let text = "cat, bat, sat, fat"; let pattern = /.at/; let matches = pattern.exec(text); console.log(matches.index); // 0 console.log(matches[0]); // cat console.log(pattern.lastIndex); // 0 matches = pattern.exec(text); console.log(matches.index); // 0 console.log(matches[0]); // cat console.log(pattern.lastIndex); // 0 The pattern in this example is not global, so each call to exec() returns the first match only ( "cat"). lastIndex remains unchanged in nonglobal mode. With the global g flag set on the pattern, each call to exec() moves further into the string looking for matches, as in this example: let text = "cat, bat, sat, fat"; let pattern = /.at/g; let matches = pattern.exec(text); console.log(matches.index); // 0 console.log(matches[0]); // cat console.log(pattern.lastIndex); // 3 matches = pattern.exec(text); console.log(matches.index); // 5 console.log(matches[0]); // bat console.log(pattern.lastIndex); // 8 matches = pattern.exec(text); console.log(matches.index); // 10 console.log(matches[0]); // sat console.log(pattern.lastIndex); // 13 This pattern is global, so each call to exec() returns the next match in the string until the end of the string is reached. Note also how the pattern's lastIndex property is affected. In global matching mode, lastIndex is incremented after each call to exec(). lastIndex tracks the index of the character that appears immediately to the right of the last match. With the sticky y flag set on the pattern, each call to exec() will search for a match in the string only at lastIndex-nowhere else. The sticky flag overrides the global flag. let text = "cat, bat, sat, fat"; let pattern = /.at/y; let matches = pattern.exec(text); console.log(matches.index); // 0 console.log(matches[0]); // cat console.log(pattern.lastIndex); // 3 There is no match starting at character index 3, so exec() will return null. exec() finding no matches resets lastIndex to 0 let text = "cat, bat, sat, fat"; let pattern = /.at/y; matches = pattern.exec(text); matches = pattern.exec(text); console.log(matches); // null console.log(pattern.lastIndex); // 0 Advancing lastIndex will allow a sticky regex exec() to find the next match: let text = "cat, bat, sat, fat"; let pattern = /.at/y; matches = pattern.exec(text); pattern.lastIndex = 5; matches = pattern.exec(text); console.log(matches.index); // 5 console.log(matches[0]); // bat console.log(pattern.lastIndex); // 8

RegExp Instance Methods test()

Regular expressions test() accepts a string argument and returns true if the pattern matches the argument and false if it does not. We can use it test if a pattern is matched without needing the actual matched text. The test() method is often used in if statements, such as the following: let text = "000-00-0000"; let pattern = /\d{3}-\d{2}-\d{4}/; if (pattern.test(text)) { console.log("The pattern was matched."); } In this example, the regular expression tests for a specific numeric sequence. If the input text matches the pattern, then a message is displayed.

RegExp Constructor Properties

The RegExp constructor function has several properties. These would be considered static properties in other languages. These properties apply to all regular expressions that are in scope, and they change based on the last regular-expression operation that was performed. They can be accessed in two different ways:a verbose property name and a shorthand name. The RegExp constructor properties are listed in the following table.
Verbose Name Short Name Description
input $_ The last string matched against.
lastMatch $& The last matched text.
lastParen $+ The last matched capturing group.
leftContext $` The text that appears in the input string prior to lastMatch.
rightContext $' The text that appears in the input string after lastMatch.
These properties can be used to extract specific information about the operation performed by exec() or test(). Consider this example: let text = "this is a test from demo2s.com"; let pattern = /(.)test/g; if (pattern.test(text)) { console.log(RegExp.input); console.log(RegExp.leftContext); console.log(RegExp.rightContext); console.log(RegExp.lastMatch); console.log(RegExp.lastParen); } Output: This code creates a pattern that searches for any character followed by "test" and puts a capturing group around the first letter. The various properties are used as follows: The input property contains the original string. The leftContext property contains the characters of the string before the word "test". The lastMatch property contains the last string that matches the entire regular expression, which is "test". The lastParen property contains the last matched capturing group. These verbose property names can be replaced with the short property names, although you must use bracket notation to access them, as shown in the following example, because most are illegal identifiers in ECMAScript: let text = "this is a test from demo2s.com"; let pattern = /(.)test/g; if (pattern.test(text)) { console.log(RegExp.$_); console.log(RegExp["$`"]); console.log(RegExp["$'"]); console.log(RegExp["$&"]); console.log(RegExp["$+"]); console.log(RegExp["$*"]); } Output: The constructor properties can store up to nine capturing-group matches. These properties are accessed via RegExp.$1, which contains the first capturing-group match through RegExp.$9, which contains the ninth capturing-group match. These properties are filled in when calling either exec() or test(), allowing you to do things like this: let text = "corde short forgot"; let pattern = /(..)or(.)/g; if (pattern.test(text)) { console.log(RegExp.$1); console.log(RegExp.$2); } Output: In this example, a pattern with two matching groups is created and tested against a string. Even though test() simply returns a Boolean value, the properties $1 and $2 are filled in on the RegExp constructor. All these RegExp constructor properties are not part of any web standard. We should not use them in any production application.

RegExp \n Metacharacter

Example

Search for a newline character in a string: let str = "Visit Demo2s.com.\nLearn JavaScript."; let patt1 = /\n/; let result = str.search(patt1); console.log(result); Output:

Description

The \n character can find a newline character. \n returns the position where the newline character was found. If no match is found, it returns -1.

Browser Compatibility

Chrome.png Node.png Firefox.png Safari.png Edge.png Opera.png
\n Yes Yes Yes Yes Yes Yes

Syntax

new RegExp("\\n") or simply: /\n/

RegExp ?! Quantifier

Example

Do a global, case insensitive search for "is" not followed by " all": let str = "is Is this all there is"; let patt1 = /is(?! all)/gi; let result = str.match(patt1); console.log(result); Output:

Description

The ?!n quantifier matches any string that is not followed by a specific string n. We can use the ?=n quantifier to match any string that is followed by a specific string n.

Browser Compatibility

Chrome.png Node.png Firefox.png Safari.png Edge.png Opera.png
?! Yes Yes Yes Yes Yes Yes

Syntax

new RegExp("regexp(?!n)") or /regexp(?!n)/

Syntax with modifiers

new RegExp("regexp(?!n)", "g") or simply: /\regexp(?!n)/g

RegExp ?= Quantifier

Example

Do a global search for "is" followed by " all": let str = "Is this all there is"; let patt1 = /is(?= all)/; let result = str.match(patt1); console.log(result); Output:

Description

The ?=n quantifier matches any string that is followed by a specific string n. Use the ?!n quantifier to match any string that is NOT followed by a specific string n.

Browser Compatibility

Chrome.png Node.png Firefox.png Safari.png Edge.png Opera.png
?= Yes Yes Yes Yes Yes Yes

Syntax

new RegExp("regexp(?=n)") or /regexp(?=n)/

Syntax with modifiers

new RegExp("regexp(?=n)", "g") or simply: /\regexp(?=n)/g

RegExp [^0-9] Expression

JavaScript RegExp [^0-9] Expression

Example

Do a global search for the numbers that are NOT 1 to 4 in a string: let str = "123456789"; let patt1 = /[^1-4]/g; let result = str.match(patt1); console.log(result); Output:

Description

The [^0-9] expression is used to find any character that is NOT a digit. The digits inside the brackets can be any numbers or span of numbers from 0 to 9. Use the [0-9] expression to find any character between the brackets that is a digit.

Browser Compatibility

Chrome.png Node.png Firefox.png Safari.png Edge.png Opera.png
[^0-9] Yes Yes Yes Yes Yes Yes

Syntax

new RegExp("[^0-9]") or simply: /[^0-9]/

Syntax with modifiers

new RegExp("[^0-9]", "g") or simply: /\[^0-9]/g

More Examples

Do a global search for numbers that are NOT "1" in a string: let str = "12121212"; let patt1 = /[^1]/g; let result = str.match(patt1); console.log(result); Output:

Example

Do a global search for numbers that are NOT 5 to 8 in a string: let str = "123456789"; let patt1 = /[^5-8]/g; let result = str.match(patt1); console.log(result); Output:

RegExp \0 Metacharacter

Example

Search for a NUL character in a string: let str = "Visit Demo2s.com.\0Learn JavaScript."; let patt1 = /\0/; let result = str.search(patt1); console.log(result); Output:

Description

The \0 metacharacter is used to find NUL character. \0 returns the position where the NUL character was found. If no match is found, it returns -1.

Browser Compatibility

Chrome.png Node.png Firefox.png Safari.png Edge.png Opera.png
\0 Yes Yes Yes Yes Yes Yes

Syntax

new RegExp("\\0") or simply: /\0/

RegExp {X} Quantifier

Example

Do a global search for a substring that contains a sequence of four digits: let str = "100, 1000 or 10000 12345"; let patt1 = /\d{4}/g; let result = str.match(patt1); console.log(result); Output:

Description

The n{X} quantifier matches any string that contains a sequence of X n's. X must be a number.

Browser Compatibility

Chrome.png Node.png Firefox.png Safari.png Edge.png Opera.png
{X} Yes Yes Yes Yes Yes Yes

Syntax

new RegExp("n{X}") or simply: /n{X}/

Syntax with modifiers

new RegExp("n{X}", "g") or simply: /\n{X}/g

RegExp {X,} Quantifier

Example

Do a global search for a substring that contains a sequence of at least three digits: let str = "100, 1000 or 10000?"; let patt1 = /\d{3,}/g; let result = str.match(patt1); console.log(result); Output:

Description

The n{X,} quantifier matches any string that contains a sequence of at least X n's. X must be a number.

Browser Compatibility

Chrome.png Node.png Firefox.png Safari.png Edge.png Opera.png
{X,} Yes Yes Yes Yes Yes Yes

Syntax

new RegExp("n{X,}") or simply: /n{X,}/

Syntax with modifiers

new RegExp("n{X,}", "g") or simply: /\n{X,}/g

RegExp {X,Y} Quantifier

Example

Do a global search for a substring that contains a sequence of three to four digits: let str = "100, 1000 or 10000?"; let patt1 = /\d{3,4}/g; let result = str.match(patt1); console.log(result); Output:

Description

The n{X,Y} quantifier matches any string that contains a sequence of X to Y n's. X and Y must be a number.

Browser Compatibility

Chrome.png Node.png Firefox.png Safari.png Edge.png Opera.png
{X,Y} Yes Yes Yes Yes Yes Yes

Syntax

new RegExp("n{X,Y}") or simply: /n{X,Y}/

Syntax with modifiers

new RegExp("n{X,Y}", "g") or simply: /\n{X,Y}/g

RegExp \xxx Metacharacter

Example

Do a global search for octal number 127 (W) in a string: let str = "Visit Demo2s.com. Hello World!"; let patt1 = /\127/g; let result = str.match(patt1); console.log(result); Output:

Description

The \xxx character is used to find the Latin character specified by an octal number xxx. If no match is found, it returns null.

Browser Compatibility

Chrome.png Node.png Firefox.png Safari.png Edge.png Opera.png
\xxx Yes Yes Yes Yes Yes Yes

Syntax

new RegExp("\\xxx") or simply: /\xxx/

Syntax with modifiers

new RegExp("\\xxx", "g") or simply: /\xxx/g

RegExp + Quantifier

Example 1

Do a global search for at least one "o": let str = "Hellooo World! Hello Demo2s.com!"; let patt1 = /o+/g; let result = str.match(patt1); console.log(result); Output:

Description

The n+ quantifier matches any string that contains at least one n.

Browser Compatibility

Chrome.png Node.png Firefox.png Safari.png Edge.png Opera.png
+ Yes Yes Yes Yes Yes Yes

Syntax

new RegExp("n+") or simply: /n+/

Syntax with modifiers

new RegExp("n+", "g") or simply: /\n+/g

More Examples

Do a global search for at least one word character: let str = "Hellooo World! Hello Demo2s.com!"; let patt1 = /\w+/g; let result = str.match(patt1); console.log(result); Output:

RegExp \t Metacharacter

Example

Search for a tab character in a string: let str = "Visit Demo2s.com.\tLearn JavaScript."; let patt1 = /\t/; let result = str.search(patt1); console.log(result); Output:

Description

The \t metacharacter is used to find a tab character. \t returns the position where the tab character was found. If no match is found, it returns -1.

Browser Compatibility

Chrome.png Node.png Firefox.png Safari.png Edge.png Opera.png
\t Yes Yes Yes Yes Yes Yes

Syntax

new RegExp("\\t") or simply: /\t/

RegExp \uxxxx Metacharacter

Example

Do a global search for the hexadecimal number 0057 (W) in a string: let str = "Visit Demo2s.com. Hello World!"; let patt1 = /\u0057/g; let result = str.match(patt1); console.log(result); Output:

Description

The \udddd character is used to find the Unicode character specified by a hexadecimal number dddd. If no match is found, it returns null.

Browser Compatibility

Chrome.png Node.png Firefox.png Safari.png Edge.png Opera.png
\udddd Yes Yes Yes Yes Yes Yes

Syntax

new RegExp("\\udddd") or simply: /\udddd/

Syntax with modifiers

new RegExp("\\udddd", "g") or simply: /\udddd/g

RegExp \v Metacharacter

Example

Search for a vertical tab character in a string: let str = "Visit Demo2s.com.\vLearn JavaScript."; let patt1 = /\v/; let result = str.search(patt1); console.log(result); Output:

Description

The \v metacharacter can find a vertical tab character. \v returns the position where the vertical tab character was found. If no match is found, it returns -1.

Browser Compatibility

Chrome.png Node.png Firefox.png Safari.png Edge.png Opera.png
\v Yes Yes Yes Yes Yes Yes

Syntax

new RegExp("\\v") or simply: /\v/

RegExp \S Metacharacter

Example

Do a global search for non-whitespace characters in a string: let str = "Is this all there is?"; let patt1 = /\S/g; let result = str.match(patt1); console.log(result); Output:

Description

The \S metacharacter can find a non-whitespace character. A whitespace character can be: A space character A tab character A carriage return character A new line character A vertical tab character A form feed character

Browser Compatibility

Chrome.png Node.png Firefox.png Safari.png Edge.png Opera.png
\S Yes Yes Yes Yes Yes Yes

Syntax

new RegExp("\\S") or simply: /\S/

Syntax with modifiers

new RegExp("\\S", "g") or simply: /\S/g

RegExp \s Metacharacter

Example

Do a global search for whitespace characters in a string: let str = "Is this all there is?"; let patt1 = /\s/g; let result = str.match(patt1); console.log(result); Output:

Description

The \s metacharacter is used to find a whitespace character. A whitespace character can be: A space character A tab character A carriage return character A new line character A vertical tab character A form feed character

Browser Compatibility

Chrome.png Node.png Firefox.png Safari.png Edge.png Opera.png
\s Yes Yes Yes Yes Yes Yes

Syntax

new RegExp("\\s") or simply: /\s/

Syntax with modifiers

new RegExp("\\s", "g") or simply: /\s/g

RegExp \W Metacharacter

Example

Do a global search for non-word characters in a string: let str = "Give This is a test!"; let patt1 = /\W/g; let result = str.match(patt1); console.log(result); Output:

Description

The \W metacharacter is used to find a non-word character. A word character is a character from a-z, A-Z, 0-9, including the _ (underscore) character.

Browser Compatibility

Chrome.png Node.png Firefox.png Safari.png Edge.png Opera.png
\W Yes Yes Yes Yes Yes Yes

Syntax

new RegExp("\\W") or simply: /\W/

Syntax with modifiers

new RegExp("\\W", "g") or simply: /\W/g

RegExp \w Metacharacter

JavaScript RegExp \w Metacharacter

Example

Do a global search for word characters in a string: let str = "Give this is a test%!"; let patt1 = /\w/g; let result = str.match(patt1); console.log(result); Output:

Description

The \w metacharacter is used to find a word character. A word character is a character from a-z, A-Z, 0-9, including the _ (underscore) character.

Browser Compatibility

Chrome.png Node.png Firefox.png Safari.png Edge.png Opera.png
\w Yes Yes Yes Yes Yes Yes

Syntax

new RegExp("\\w") or simply: /\w/

Syntax with modifiers

new RegExp("\\w", "g") or simply: /\w/g

RegExp (x|y) Expression

JavaScript RegExp (x|y) Expression

Example

let str = "re, green, red, green, gren, gr, blue, yellow"; let patt1 = /(red|green)/g; let result = str.match(patt1); console.log(result); Output:

Description

The (x|y) expression can find any of the alternatives specified. The alternatives can be of any characters.

Browser Compatibility

Chrome.png Node.png Firefox.png Safari.png Edge.png Opera.png
(x|y) Yes Yes Yes Yes Yes Yes

Syntax

new RegExp("(x|y)") or simply: /(x|y)/

Syntax with modifiers

new RegExp("(x|y)", "g") or simply: /(x|y)/g

More Examples

let str = "01234567890123456789"; let patt1 = /(0|5|7)/g; let result = str.match(patt1); console.log(result); Output:

RegExp * Quantifier

Example 1

Do a global search for an "l", followed by zero or more "o" characters: let str = "Hellooo World! Hello Demo2s.com!"; let patt1 = /lo*/g; let result = str.match(patt1); console.log(result); Output:

Description

The n* quantifier matches any string that contains zero or more occurrences of n.

Browser Compatibility

Chrome.png Node.png Firefox.png Safari.png Edge.png Opera.png
* Yes Yes Yes Yes Yes Yes

Syntax

new RegExp("n*") or simply: /n*/

Syntax with modifiers

new RegExp("n*", "g") or simply: /\n*/g

More Examples

Do a global search for a "1", followed by zero or more "0" characters: let str = "1, 100 or 1000?"; let patt1 = /10*/g; let result = str.match(patt1); console.log(result); Output:

RegExp ? Quantifier

Example

Do a global search for a "1", followed by zero or one "0" characters: let str = "1, 100 or 1000?"; let patt1 = /10?/g; let result = str.match(patt1); console.log(result); Output:

Description

The n? quantifier matches any string that contains zero or one occurrences of n.

Browser Compatibility

Chrome.png Node.png Firefox.png Safari.png Edge.png Opera.png
? Yes Yes Yes Yes Yes Yes

Syntax

new RegExp("n?") or simply: /n?/

Syntax with modifiers

new RegExp("n?", "g") or simply: /\n?/g

Global Object

The Global object is the most unique in ECMAScript because it isn't explicitly accessible. ECMA-262 specifies the Global object as a catch all for properties and methods that don't otherwise have an owning object. In Javascript, there is no global variables or global functions. All variables and functions defined globally become properties of the Global object. Functions such as isNaN(), isFinite(), parseInt(), and parseFloat(), are actually methods of the Global object. Global object also has methods for encoding and decoding URL. It also has an eval function.

URI-Encoding and Decoding Methods

Encode URL

The encodeURI() and encodeURIComponent() methods are used to encode URIs (Uniform Resource Identifiers) to be passed to the browser. To be valid, a URI cannot contain certain characters, such as spaces. The URI-encoding methods encode the URIs so that a browser can still accept and understand them. It replaces all invalid characters with a special UTF-8 encoding. The encodeURI() method works on an entire URI. The encodeURI() does not encode special characters that are part of a URI, such as the colon, forward slash, question mark, and pound sign. The encodeURIComponent() methods work on a segment of a URI. encodeURIComponent() encodes every non-standard character it finds. Consider this example: let uri = "https://www.demo2s.com/index page.html#start"; console.log(encodeURI(uri)); console.log(encodeURIComponent(uri)); Output: Here, using encodeURI() left the value completely intact except for the space, which was replaced with %20. The encodeURIComponent() method replaced all non-alphanumeric characters with their encoded equivalents. This is why encodeURI() can be used on full URIs, whereas encodeURIComponent() can be used only on strings that are appended to the end of an existing URI. You'll use encodeURIComponent() much more frequently than encodeURI() because it's more common to encode query string arguments separately from the base URI.

Decode URL

The two counterparts to encodeURI() and encodeURIComponent() are decodeURI() and decodeURIComponent(). The decodeURI() method decodes only characters that would have been replaced by using encodeURI(). For instance, %20 is replaced with a space, but %23 is not replaced because it represents a pound sign (#), which encodeURI() does not replace. decodeURIComponent() decodes all characters encoded by encodeURIComponent(), essentially meaning it decodes all special values. Consider this example: let uri = "https%3A%2F%2Fwww.demo2s.com%2Findex%20page.html%23start"; console.log(decodeURI(uri)); console.log(decodeURIComponent(uri)); Output: Here, the uri variable contains a string that is encoded using encodeURIComponent(). The first value output is the result of decodeURI(), which replaced only the %20 with a space. The second value is the output of decodeURIComponent(), which replaces all the special characters and outputs a string that has no escaping in it. The URI methods encodeURI(), encodeURIComponent(), decodeURI(), and decodeURIComponent() replace the escape() and unescape() methods, which are deprecated in the ECMA-262 third edition. Avoid using escape() and unescape() in production code.

eval() Method

ECMAScript eval() method works like an entire ECMAScript interpreter and accepts one argument, a string of ECMAScript to execute. Here's an example: eval("console.log('hi')"); This line is functionally equivalent to the following: console.log("hi"); When the interpreter finds an eval() call, it interprets the argument into actual ECMAScript statements and then inserts it into place. Code executed by eval() is considered to be part of the execution context in which the call is made, and the executed code has the same scope chain as that context. This means variables that are defined in the containing context can be referenced inside an eval() call, such as in this example: let msg = "hello world"; eval("console.log(msg)"); // "hello world" Here, the variable msg is defined outside the context of the eval() call, yet the call to console.log() still displays the text "hello world" because the second line is replaced with a real line of code. Likewise, you can define a function or variables inside an eval() call that can be referenced by the code outside, as follows: eval("function sayHi() { console.log('hi'); }"); sayHi(); Here, the sayHi() function is defined inside an eval() call. Because that call is replaced with the actual function, it is possible to call sayHi() on the following line. This works the same for variables: eval("let msg = 'hello world';"); console.log(msg); // "hello world" Any variables or functions created inside of eval() will not be hoisted, as they are contained within a string when the code is being parsed. They are created only at the time of eval() execution. In strict mode, variables and functions created inside of eval() are not accessible outside. Also, in strict mode, assigning a value to eval causes an error: "use strict"; eval = "hi"; // causes error

Note

eval() might compromise your site or application security.

Global Object Properties

The Global object has a number of properties, some of which have already been mentioned in this book. The special values of undefined, NaN, and Infinity are all properties of the Global object. Additionally, all native reference type constructors, such as Object and Function, are properties of the Global object. The following table lists all of the properties.
Property Description
undefined The special value undefined
NaN The special value NaN
Infinity The special value Infinity
Object Constructor for Object
Array Constructor for Array
Function Constructor for Function
Boolean Constructor for Boolean
String Constructor for String
Number Constructor for Number
Date Constructor for Date
RegExp Constructor for RegExp
Symbol Pseudo-constructor for Symbol
Error Constructor for Error
EvalError Constructor for EvalError
RangeError Constructor for RangeError
ReferenceError Constructor for ReferenceError
SyntaxError Constructor for SyntaxError
TypeError Constructor for TypeError
URIError Constructor for URIError

Window Object

Though ECMA-262 doesn't indicate a way to access the Global object directly, web browsers implement it such that the window is the Global object's delegate. Therefore, all variables and functions declared in the global scope become properties on window. Consider this example: let color = "red"; function sayColor() { console.log(window.color); } window.sayColor(); // "red" Here, a global variable named color and a global function named sayColor() are defined. Inside sayColor(), the color variable is accessed via window.color to show that the global variable became a property of window. The function is then called directly off of the window object as window.sayColor(), which pops up the console.log.

Get access to Global object

To retrieve the Global object is to use the following code: abc = 'a'; let global = function() { return this; }(); console.log(abc); Output: This code creates an immediately-invoked function expression that returns the value of this. The this value is equivalent to the Global object when a function is executed with no explicit this value specified either by being an object method or via call()/apply(). Thus, calling a function that simply returns this is a consistent way to retrieve the Global object in any execution environment.

Math Object

ECMAScript provides the Math object as a common location for mathematical formulas, information, and computation. The Math object offers a number of properties and methods to help these computations.

Math Object Properties

The Math object has several properties, consisting mostly of special values in the world of mathematics. The following table describes these properties.
Property Description
Math.E The value of e, the base of the natural logarithms
Math.LN10 The natural logarithm of 10
Math.LN2 The natural logarithm of 2
Math.LOG2E The base 2 logarithm of e
Math.LOG10E The base 10 logarithm of e
Math.PI The value of p
Math.SQRT1_2 The square root of 0.5
Math.SQRT2 The square root of 2
The Math object has a lot of methods related to various simple and higher-level mathematical operations. The following table enumerates some methods of the Math object.
Method Description
Math.abs(x) Returns the absolute value of x
Math.exp(x) Returns Math.E raised to the power of x
Math.expm1(x) Equivalent to Math.exp(x) - 1
Math.log(x) Returns the natural logarithm of x
Math.log1p(x) Equivalent to 1 + Math.log(x)
Math.pow(x, power) Returns x raised to the power of power
Math.pow (...numbers) Returns the square root of the sum of the squares of each number in numbers
Math.clz32 (x) Returns the number of leading zeroes of a 32-bit integer x
Math.sign(x) Returns 1, 0, -0, or -1 indicating the sign of x
Math.trunc(x) Returns the integer component of x, removing any decimals
Math.sqrt(x) Returns the square root of x
Math.cbrt(x) Returns the cubic root of x
Math.acos(x) Returns the arc cosine of x
Math.acosh(x) Returns the hyperbolic arc cosine of x
Math.asin(x ) Returns the arc sine of x
Math.asin(x ) Returns the hyperbolic arc sine of x
Math.atan(x ) Returns the arc tangent of x
Math.atanh(x ) Returns the hyperbolic arc tangent of x
Math.atan2(y, x ) Returns the arc tangent of y/x
Math.cos(x ) Returns the cosine of x
Math.sin(x ) Returns the sine of x
Math.tan(x ) Returns the tangent of x

Math min() and max() Methods

The Math object contains many methods aimed at performing both simple and complex mathematical calculations. The min() and max() methods determine which number is the smallest or largest in a group of numbers. These methods accept any number of parameters, as shown in the following example: let max = Math.max(3, 5, 2, 1); console.log(max); // 5 let min = Math.min(3, 5, 32, 16); console.log(min); // 3 These methods are useful for avoiding extra loops and if statements to determine the maximum value out of a group of numbers. To find the maximum or the minimum value in an array, you can use the spread operator as follows: let values = [1, 2, 3, 4, 5, 6, 7, 8]; let max = Math.max(...values); console.log(max); Output:

Math Rounding Methods

Four methods-Math.ceil(), Math.floor(), Math.round(), and Math.fround()-handle rounding in different ways as described here:
Method Description
Math.ceil() rounds numbers up to the nearest integer value.
Math.floor() always rounds numbers down to the nearest integer value.
Math.round() rounds up if the number is at least halfway to the next integer value (0.5 or higher) and rounds down if not.
Math.fround() returns the nearest single precision (32 bits) floating point representation of the number.
The following example illustrates how these methods work: console.log(Math.ceil(25.9)); // 26 console.log(Math.ceil(25.5)); // 26 console.log(Math.ceil(25.1)); // 26 console.log(Math.round(25.9)); // 26 console.log(Math.round(25.5)); // 26 console.log(Math.round(25.1)); // 25 console.log(Math.fround(0.4)); // 0.4000000059604645 console.log(Math.fround(0.5)); // 0.5 console.log(Math.fround(25.9)); // 25.899999618530273 console.log(Math.floor(25.9)); // 25 console.log(Math.floor(25.5)); // 25 console.log(Math.floor(25.1)); // 25 For all values between 25 and 26 (exclusive), Math.ceil() always returns 26 because it will always round up. The Math.round() method returns 26 only if the number is 25.5 or greater; otherwise it returns 25. Last, Math.floor() returns 25 for all numbers between 25 and 26 (exclusive).

Math random() Method

The Math.random() method returns a random number between the 0 and the 1, not including either 0 or 1. You can use Math.random() to select numbers within a certain integer range by using the following formula: number = Math.floor(Math.random() * total_number_of_choices + first_possible_value) The Math.floor() method is used here because Math.random() always returns a decimal value, meaning that multiplying it by a number and adding another still yields a decimal value. So, if you wanted to select a number between 1 and 10, the code would look like this: let num = Math.floor(Math.random() * 10 + 1); You see 10 possible values (1 through 10), with the first possible value being 1. If you want to select a number between 2 and 10, then the code would look like this: let num = Math.floor(Math.random() * 9 + 2); There are only nine numbers when counting from 2 to 10, so the total number of choices is nine, with the first possible value being 2. The following code defines a function to select random value within a range. function selectFrom(lowerValue, upperValue) { let choices = upperValue - lowerValue + 1; return Math.floor(Math.random() * choices + lowerValue); } let num = selectFrom(2,10); console.log(num); // number between 2 and 10, inclusive Here, the function selectFrom() accepts two arguments: the lowest value that should be returned and the highest value that should be returned. The number of choices is calculated by subtracting the two values and adding one and then applying the previous formula to those numbers. So it's possible to select a number between 2 and 10 (inclusive) by calling selectFrom(2,10).

Random item

Using the function, it's easy to select a random item from an array, as shown here: let colors = ["red", "green", "blue", "yellow", "black", "purple", "brown"]; let color = colors[selectFrom(0, colors.length-1)]; console.log(color); Output: In this example, the second argument to selectFrom() is the length of the array minus 1, which is the last position in an array.

Define Object Type

Object type is one of the most commonly used types in ECMAScript. Instances of Object are suited to storing and transmitting data around an application. There are two ways to explicitly create an instance of Object.

Object constructor

The first is to use the new operator with the Object constructor like this: let person = new Object(); person.name = "CSS"; person.age = 19;

Object literal

The other way is to use object literal notation. Object literal notation is a shorthand form of object definition designed to simplify creating an object with numerous properties. For example, the following defines the same person object from the previous example using object literal notation: let person = { name: "CSS", age: 19 }; In this example, the left curly brace ( {) signifies the beginning of an object literal because it occurs in an expression context. A comma is used to separate properties in an object literal, so there's a comma after the string "CSS" but not after the value 19 because age is the last property in the object.

Property names

Property names can also be specified as strings or numbers when using object literal notation, such as in this example: let person = { "name": "CSS", "age": 29, 5: true }; This example produces an object with a name property, an age property, and a property 5. The numeric property names are automatically converted to strings.

Empty object

It's also possible to create an object with only the default properties and methods using object literal notation by leaving the space between the curly braces empty, such as this: let person = {}; // same as new Object() person.name = "CSS"; person.age = 29; Prefer to use object literal notation only when you're going to specify properties for readability. When defining an object via object literal notation, the Object constructor is never actually called. We should use object literal notation because it requires less code and visually encapsulates all related data. The object literals are a preferred way of passing a large number of optional arguments to a function, such as in this example: function displayInfo(args) { let output = ""; if (typeof args.name == "string"){ output += "Name: " + args.name + "\n"; }// w w w . d e m o 2 s .c o m if (typeof args.age == "number") { output += "Age: " + args.age + "\n"; } console.log(output); } displayInfo({ name: "CSS", age: 19 }); displayInfo({ name: "HTML" }); Output: Here, the function displayInfo() accepts a single argument named args. The argument may come in with a property called name or age or both or neither of those. The function tests for the existence of each property using the typeof operator and then to construct a message to display based on availability. This function is then called twice, each time with different data specified in an object literal. The function works correctly in both cases. This pattern for argument passing is best used when there are a large number of optional arguments that can be passed into the function. The best approach is to use named arguments for those that are required and an object literal to encompass multiple optional arguments.

Object Type Bracket notation

Although object properties are typically accessed using dot notation, which is common to many object-oriented languages, it's also possible to access properties via bracket notation. When you use bracket notation, a string containing the property name is placed between the brackets, as in this example: let person = { name: "CSS", age: 19 }; console.log(person["name"]); // "CSS" console.log(person.name); // "CSS" Functionally, there is no difference between the two approaches. The main advantage of bracket notation is that it allows you to use variables for property access, as in this example: let propertyName = "name"; console.log(person[propertyName]); // "CSS" You can also use bracket notation when the property name contains characters that would be either a syntax error or a keyword/reserved word. For example: person["first name"] = "CSS"; Because the name "first name" contains a space, you can't use dot notation to access it. The property names can contain non alphanumeric characters-you just need to use bracket notation to access them. Generally speaking, dot notation is preferred unless variables are necessary to access properties by name.

Object Introduction

ECMA-262 defines an object as an unordered collection of properties. Each property or method in an object is identified by a name that is mapped to a value. We can think of ECMAScript objects as hash tables: a grouping of name-value pairs where the value may be data or a function. The canonical way of creating a custom object is to create a new instance of Object and add properties and methods to it, as in this example: let person = new Object(); person.name = "CSS"; person.age = 19; person.job = "Style"; person.sayName = function() { console.log(this.name); }; This example creates an object called person that has three properties:name, age, and job, and one method sayName(). The sayName() method displays the value of this.name, which resolves to person.name. We can also use object literals to create objects. The previous example can be rewritten using object literal notation as follows: let person = { name: "CSS", age: 19, job: "Style", sayName() { console.log(this.name); } }; The person object in this example is equivalent to the person object in the prior example, with all the same properties and methods. These properties are all created with certain characteristics that define their behavior in JavaScript.

Object Getter and Setter

We can add getter and setter to object when creating objects: let person = { name_: '', get name() { return this.name_; }, set name(name) { this.name_ = name; }, sayName() { console.log('My name is ${this.name_}'); } }; person.name = 'HTML'; person.sayName(); // My name is HTML

Object Types of Properties

ECMA-262 describes characteristics of properties through the use of internal-only attributes. These attributes are defined by the specification for implementation in JavaScript engines. These attributes are not directly accessible in JavaScript. To indicate that an attribute is internal, surround the attribute name with two pairs of square brackets, such as [[Enumerable]]. There are two types of properties: data properties and accessor properties.