The post-rerank relevance floor rejects noise but passes coherent off-topic questions, and it gets worse as the library grows. Adding Gutenberg prose, the Python tutorial and MDN pages to the eval corpus took the off-topic leak rate from 22% to 67% at the 0.62 floor. Score cutoff does not fix it. The weakest correct top hit scores 0.666 while "write me a haiku about autumn leaves" pulls Walden at 0.705. A cosine floor and a top-vs-median contrast test both cost recall faster than they cut leaks. - Add rag.relevanceCheck (default off). After the floor, the tasks model, or the chat model when none is set, gives one grammar-constrained on_topic verdict for the whole retrieved set. Off topic means no context and no citations. Any failure keeps the chunks unjudged. - Add a Settings > Models toggle recommending an 8B+ tasks model. On llama3:8b the check cuts off-topic leaks from 67% to 6% for about 5 points of core recall@5 and ~0.6s per turn. On 3B models it rejects most valid questions, which is why it is opt-in. - Add 6 distractor documents and an off_topic suite (18 unanswerable questions, 7 distractor controls) to the eval corpus. - Report per-tag leak rates and leaked cases with top scores in eval:retrieval. Add --judge-model to measure the check and --dump to export pre-floor candidate pools for offline gate simulation. - Pin the check off in eval:generation so a local setting cannot move scores. - Record the distractor sweep on RAG_MIN_FINAL_SCORE. The 0.62 default stays: 0.65 halves leaks at no measured recall cost but leaves only 0.016 of margin. The corpus fingerprint changes to b4ebe5dce8699f5d. Retrieval reports from before this commit are not comparable, and a new baseline is committed. Refs #1341
107 KiB
MDN JavaScript Guide (excerpt)
Distractor document for the #1341 off-topic eval suite. From MDN Web Docs (github.com/mdn/content), by Mozilla Contributors, CC-BY-SA 2.5.
{{PreviousNext("Web/JavaScript/Guide/Loops_and_iteration", "Web/JavaScript/Guide/Expressions_and_operators")}}
Functions are one of the fundamental building blocks in JavaScript. A function in JavaScript is similar to a procedure—a set of statements that performs a task or calculates a value, but for a procedure to qualify as a function, it should take some input and return an output where there is some obvious relationship between the input and the output. To use a function, you must define it somewhere in the scope from which you wish to call it.
See also the exhaustive reference chapter about JavaScript functions to get to know the details.
Defining functions
Function declarations
A function definition (also called a function declaration, or function statement) consists of the function keyword, followed by:
- The name of the function.
- A list of parameters to the function, enclosed in parentheses and separated by commas.
- The JavaScript statements that define the function, enclosed in curly braces,
{ /* … */ }.
For example, the following code defines a function named square:
function square(number) {
return number * number;
}
The function square takes one parameter, called number. The function consists of one statement that says to return the parameter of the function (that is, number) multiplied by itself. The return statement specifies the value returned by the function, which is number * number.
Parameters are essentially passed to functions by value — so if the code within the body of a function assigns a completely new value to a parameter that was passed to the function, the change is not reflected globally or in the code which called that function.
When you pass an object as a parameter, if the function changes the object's properties, that change is visible outside the function, as shown in the following example:
function myFunc(theObject) {
theObject.make = "Toyota";
}
const myCar = {
make: "Honda",
model: "Accord",
year: 1998,
};
console.log(myCar.make); // "Honda"
myFunc(myCar);
console.log(myCar.make); // "Toyota"
When you pass an array as a parameter, if the function changes any of the array's values, that change is visible outside the function, as shown in the following example:
function myFunc(theArr) {
theArr[0] = 30;
}
const arr = [45];
console.log(arr[0]); // 45
myFunc(arr);
console.log(arr[0]); // 30
Function declarations and expressions can be nested, which forms a scope chain. For example:
function addSquares(a, b) {
function square(x) {
return x * x;
}
return square(a) + square(b);
}
See function scopes and closures for more information.
Function expressions
While the function declaration above is syntactically a statement, functions can also be created by a function expression.
Such a function can be anonymous; it does not have to have a name. For example, the function square could have been defined as:
const square = function (number) {
return number * number;
};
console.log(square(4)); // 16
However, a name can be provided with a function expression. Providing a name allows the function to refer to itself, and also makes it easier to identify the function in a debugger's stack traces:
const factorial = function fac(n) {
return n < 2 ? 1 : n * fac(n - 1);
};
console.log(factorial(3)); // 6
Function expressions are convenient when passing a function as an argument to another function. The following example defines a map function that should receive a function as first argument and an array as second argument. Then, it is called with a function defined by a function expression:
function map(f, a) {
const result = new Array(a.length);
for (let i = 0; i < a.length; i++) {
result[i] = f(a[i]);
}
return result;
}
const numbers = [0, 1, 2, 5, 10];
const cubedNumbers = map(function (x) {
return x * x * x;
}, numbers);
console.log(cubedNumbers); // [0, 1, 8, 125, 1000]
In JavaScript, a function can be defined based on a condition. For example, the following function definition defines myFunc only if num equals 0:
let myFunc;
if (num === 0) {
myFunc = function (theObject) {
theObject.make = "Toyota";
};
}
In addition to defining functions as described here, you can also use the {{jsxref("Function")}} constructor to create functions from a string at runtime, much like {{jsxref("Global_Objects/eval", "eval()")}}.
A method is a function that is a property of an object. Read more about objects and methods in Working with objects.
Calling functions
Defining a function does not execute it. Defining it names the function and specifies what to do when the function is called.
Calling the function actually performs the specified actions with the indicated parameters. For example, if you define the function square, you could call it as follows:
square(5);
The preceding statement calls the function with an argument of 5. The function executes its statements and returns the value 25.
Functions must be in scope when they are called, but the function declaration can be hoisted (appear below the call in the code). The scope of a function declaration is the function in which it is declared (or the entire program, if it is declared at the top level).
The arguments of a function are not limited to strings and numbers. You can pass whole objects to a function. The showProps() function (defined in Working with objects) is an example of a function that takes an object as an argument.
A function can call itself. For example, here is a function that computes factorials recursively:
function factorial(n) {
if (n === 0 || n === 1) {
return 1;
}
return n * factorial(n - 1);
}
You could then compute the factorials of 1 through 5 as follows:
console.log(factorial(1)); // 1
console.log(factorial(2)); // 2
console.log(factorial(3)); // 6
console.log(factorial(4)); // 24
console.log(factorial(5)); // 120
There are other ways to call functions. There are often cases where a function needs to be called dynamically, or the number of arguments to a function vary, or in which the context of the function call needs to be set to a specific object determined at runtime.
It turns out that functions are themselves objects — and in turn, these objects have methods. (See the {{jsxref("Function")}} object.) The call() and apply() methods can be used to achieve this goal.
Function hoisting
Consider the example below:
console.log(square(5)); // 25
function square(n) {
return n * n;
}
This code runs without any error, despite the square() function being called before it's declared. This is because the JavaScript interpreter hoists the entire function declaration to the top of the current scope, so the code above is equivalent to:
// All function declarations are effectively at the top of the scope
function square(n) {
return n * n;
}
console.log(square(5)); // 25
Function hoisting only works with function declarations — not with function expressions. The following code will not work:
console.log(square(5)); // ReferenceError: Cannot access 'square' before initialization
const square = function (n) {
return n * n;
};
Recursion
A function can refer to and call itself. It can be referred to either by the function expression or declaration's name, or via any in-scope variable that refers to the function object. For example, consider the following function definition:
const foo = function bar() {
// statements go here
};
Within the function body, you can refer to the function itself either as bar or foo, and call itself using bar() or foo().
A function that calls itself is called a recursive function. In some ways, recursion is analogous to a loop. Both execute the same code multiple times, and both require a condition (to avoid an infinite loop, or rather, infinite recursion in this case).
For example, consider the following loop:
let x = 0;
// "x < 10" is the loop condition
while (x < 10) {
// do stuff
x++;
}
It can be converted into a recursive function declaration, followed by a call to that function:
function loop(x) {
// "x >= 10" is the exit condition (equivalent to "!(x < 10)")
if (x >= 10) {
return;
}
// do stuff
loop(x + 1); // the recursive call
}
loop(0);
However, some algorithms cannot be simple iterative loops. For example, getting all the nodes of a tree structure (such as the DOM) is easier via recursion:
function walkTree(node) {
if (node === null) {
return;
}
// do something with node
for (const child of node.childNodes) {
walkTree(child);
}
}
Compared to the function loop, each recursive call itself makes many recursive calls here.
It is possible to convert any recursive algorithm to a non-recursive one, but the logic is often much more complex, and doing so requires the use of a stack.
In fact, recursion itself uses a stack: the function stack. The stack-like behavior can be seen in the following example:
function foo(i) {
if (i < 0) {
return;
}
console.log(`begin: ${i}`);
foo(i - 1);
console.log(`end: ${i}`);
}
foo(3);
// Logs:
// begin: 3
// begin: 2
// begin: 1
// begin: 0
// end: 0
// end: 1
// end: 2
// end: 3
Immediately Invoked Function Expressions (IIFE)
An Immediately Invoked Function Expression (IIFE) is a code pattern that directly calls a function defined as an expression. It looks like this:
(function () {
// Do something
})();
const value = (function () {
// Do something
return someValue;
})();
Instead of saving the function in a variable, the function is immediately invoked. This is almost equivalent to just writing the function body, but there are a few unique benefits:
- It creates an extra scope of variables, which helps to confine variables to the place where they are useful.
- It is now an expression instead of a sequence of statements. This allows you to write complex computation logic when initializing variables.
For more information, see the IIFE glossary entry.
Function scopes and closures
Functions form a scope for variables—this means variables defined inside a function cannot be accessed from anywhere outside the function. The function scope inherits from all the upper scopes. For example, a function defined in the global scope can access all variables defined in the global scope. A function defined inside another function can also access all variables defined in its parent function, and any other variables to which the parent function has access. On the other hand, the parent function (and any other parent scope) does not have access to the variables and functions defined inside the inner function. This provides a sort of encapsulation for the variables in the inner function.
// The following variables are defined in the global scope
const num1 = 20;
const num2 = 3;
const name = "Chamakh";
// This function is defined in the global scope
function multiply() {
return num1 * num2;
}
console.log(multiply()); // 60
// A nested function example
function getScore() {
const num1 = 2;
const num2 = 3;
function add() {
return `${name} scored ${num1 + num2}`;
}
return add();
}
console.log(getScore()); // "Chamakh scored 5"
Closures
We also refer to the function body as a closure. A closure is any piece of source code (most commonly, a function) that refers to some variables, and the closure "remembers" these variables even when the scope in which these variables were declared has exited.
Closures are usually illustrated with nested functions to show that they remember variables beyond the lifetime of its parent scope; but in fact, nested functions are unnecessary. Technically speaking, all functions in JavaScript form closures—some just don't capture anything, and closures don't even have to be functions. The key ingredients for a useful closure are the following:
- A parent scope that defines some variables or functions. It should have a clear lifetime, which means it should finish execution at some point. Any scope that's not the global scope satisfies this requirement; this includes blocks, functions, modules, and more.
- An inner scope defined within the parent scope, which refers to some variables or functions defined in the parent scope.
- The inner scope manages to survive beyond the lifetime of the parent scope. For example, it is saved to a variable that's defined outside the parent scope, or it's returned from the parent scope (if the parent scope is a function).
- Then, when you call the function outside of the parent scope, you can still access the variables or functions that were defined in the parent scope, even though the parent scope has finished execution.
The following is a typical example of a closure:
// The outer function defines a variable called "name"
const pet = function (name) {
const getName = function () {
// The inner function has access to the "name" variable of the outer function
return name;
};
return getName; // Return the inner function, thereby exposing it to outer scopes
};
const myPet = pet("Vivie");
console.log(myPet()); // "Vivie"
It can be much more complex than the code above. An object containing methods for manipulating the inner variables of the outer function can be returned.
const createPet = function (name) {
let sex;
const pet = {
// setName(newName) is equivalent to setName: function (newName)
// in this context
setName(newName) {
name = newName;
},
getName() {
return name;
},
getSex() {
return sex;
},
setSex(newSex) {
if (
typeof newSex === "string" &&
(newSex.toLowerCase() === "male" || newSex.toLowerCase() === "female")
) {
sex = newSex;
}
},
};
return pet;
};
const pet = createPet("Vivie");
console.log(pet.getName()); // Vivie
pet.setName("Oliver");
pet.setSex("male");
console.log(pet.getSex()); // male
console.log(pet.getName()); // Oliver
In the code above, the name variable of the outer function is accessible to the inner functions, and there is no other way to access the inner variables except through the inner functions. The inner variables of the inner functions act as safe stores for the outer arguments and variables. They hold "persistent" and "encapsulated" data for the inner functions to work with. The functions do not even have to be assigned to a variable, or have a name.
const getCode = (function () {
const apiCode = "0]Eal(eh&2"; // A code we do not want outsiders to be able to modify…
return function () {
return apiCode;
};
})();
console.log(getCode()); // "0]Eal(eh&2"
In the code above, we use the IIFE pattern. Within this IIFE scope, two values exist: a variable apiCode and an unnamed function that gets returned and gets assigned to the variable getCode. apiCode is in the scope of the returned unnamed function but not in the scope of any other part of the program, so there is no way for reading the value of apiCode apart from via the getCode function.
Multiply-nested functions
Functions can be multiply-nested. For example:
- A function (
A) contains a function (B), which itself contains a function (C). - Both functions
BandCform closures here. So,Bcan accessA, andCcan accessB. - In addition, since
Ccan accessBwhich can accessA,Ccan also accessA.
Thus, the closures can contain multiple scopes; they recursively contain the scope of the functions containing it. This is called scope chaining. Consider the following example:
function A(x) {
function B(y) {
function C(z) {
console.log(x + y + z);
}
C(3);
}
B(2);
}
A(1); // Logs 6 (which is 1 + 2 + 3)
In this example, C accesses B's y and A's x. This can be done because:
Bforms a closure includingA(i.e.,Bcan accessA's arguments and variables).Cforms a closure includingB.- Because
C's closure includesBandB's closure includesA, thenC's closure also includesA. This meansCcan access bothBandA's arguments and variables. In other words,Cchains the scopes ofBandA, in that order.
The reverse, however, is not true. A cannot access C, because A cannot access any argument or variable of B, which C is a variable of. Thus, C remains private to only B.
Name conflicts
When two arguments or variables in the scopes of a closure have the same name, there is a name conflict. More nested scopes take precedence. So, the innermost scope takes the highest precedence, while the outermost scope takes the lowest. This is the scope chain. The first on the chain is the innermost scope, and the last is the outermost scope. Consider the following:
function outside() {
const x = 5;
function inside(x) {
return x * 2;
}
return inside;
}
console.log(outside()(10)); // 20 (instead of 10)
The name conflict happens at the statement return x * 2 and is between inside's parameter x and outside's variable x. The scope chain here is inside => outside => global object. Therefore, inside's x takes precedences over outside's x, and 20 (inside's x) is returned instead of 10 (outside's x).
Using the arguments object
The arguments of a function are maintained in an array-like object. Within a function, you can address the arguments passed to it as follows:
arguments[i];
where i is the ordinal number of the argument, starting at 0. So, the first argument passed to a function would be arguments[0]. The total number of arguments is indicated by arguments.length.
Using the arguments object, you can call a function with more arguments than it is formally declared to accept. This is often useful if you don't know in advance how many arguments will be passed to the function. You can use arguments.length to determine the number of arguments actually passed to the function, and then access each argument using the arguments object.
For example, consider a function that concatenates several strings. The only formal argument for the function is a string that specifies the characters that separate the items to concatenate. The function is defined as follows:
function myConcat(separator) {
let result = ""; // initialize list
// iterate through arguments
for (let i = 1; i < arguments.length; i++) {
result += arguments[i] + separator;
}
return result;
}
You can pass any number of arguments to this function, and it concatenates each argument into a string "list":
console.log(myConcat(", ", "red", "orange", "blue"));
// "red, orange, blue, "
console.log(myConcat("; ", "elephant", "giraffe", "lion", "cheetah"));
// "elephant; giraffe; lion; cheetah; "
console.log(myConcat(". ", "sage", "basil", "oregano", "pepper", "parsley"));
// "sage. basil. oregano. pepper. parsley. "
Note
The
argumentsvariable is "array-like", but not an array. It is array-like in that it has a numbered index and alengthproperty. However, it does not possess all of the array-manipulation methods.
See the {{jsxref("Function")}} object in the JavaScript reference for more information.
Function parameters
There are two special kinds of parameter syntax: default parameters and rest parameters.
Default parameters
In JavaScript, parameters of functions default to undefined. However, in some situations it might be useful to set a different default value. This is exactly what default parameters do.
In the past, the general strategy for setting defaults was to test parameter values in the body of the function and assign a value if they are undefined.
In the following example, if no value is provided for b, its value would be undefined when evaluating a*b, and a call to multiply would normally have returned NaN. However, this is prevented by the second line in this example:
function multiply(a, b) {
b = typeof b !== "undefined" ? b : 1;
return a * b;
}
console.log(multiply(5)); // 5
With default parameters, a manual check in the function body is no longer necessary. You can put 1 as the default value for b in the function head:
function multiply(a, b = 1) {
return a * b;
}
console.log(multiply(5)); // 5
For more details, see default parameters in the reference.
Rest parameters
The rest parameter syntax allows us to represent an indefinite number of arguments as an array.
In the following example, the function multiply uses rest parameters to collect arguments from the second one to the end. The function then multiplies these by the first argument.
function multiply(multiplier, ...theArgs) {
return theArgs.map((x) => multiplier * x);
}
const arr = multiply(2, 1, 2, 3);
console.log(arr); // [2, 4, 6]
Arrow functions
An arrow function expression (also called a fat arrow to distinguish from a hypothetical -> syntax in future JavaScript) has a shorter syntax compared to function expressions and does not have its own this, arguments, super, or new.target. Arrow functions are always anonymous.
Two factors influenced the introduction of arrow functions: shorter functions and non-binding of this.
Shorter functions
In some functional patterns, shorter functions are welcome. Compare:
const a = ["Hydrogen", "Helium", "Lithium", "Beryllium"];
const a2 = a.map(function (s) {
return s.length;
});
console.log(a2); // [8, 6, 7, 9]
const a3 = a.map((s) => s.length);
console.log(a3); // [8, 6, 7, 9]
No separate this
Until arrow functions, every new function defined its own this value (a new object in the case of a constructor, undefined in strict mode function calls, the base object if the function is called as an "object method", etc.). This proved to be less than ideal with an object-oriented style of programming.
function Person() {
// The Person() constructor defines `this` as itself.
this.age = 0;
setInterval(function growUp() {
// In nonstrict mode, the growUp() function defines `this`
// as the global object, which is different from the `this`
// defined by the Person() constructor.
this.age++;
}, 1000);
}
const p = new Person();
In ECMAScript 3/5, this issue was fixed by assigning the value in this to a variable that could be closed over.
function Person() {
// Some choose `that` instead of `self`.
// Choose one and be consistent.
const self = this;
self.age = 0;
setInterval(function growUp() {
// The callback refers to the `self` variable of which
// the value is the expected object.
self.age++;
}, 1000);
}
Alternatively, a bound function could be created so that the proper this value would be passed to the growUp() function.
An arrow function does not have its own this; the this value of the enclosing execution context is used. Thus, in the following code, the this within the function that is passed to setInterval has the same value as this in the enclosing function:
function Person() {
this.age = 0;
setInterval(() => {
this.age++; // `this` properly refers to the person object
}, 1000);
}
const p = new Person();
{{PreviousNext("Web/JavaScript/Guide/Loops_and_iteration", "Web/JavaScript/Guide/Expressions_and_operators")}}
{{PreviousNext("Web/JavaScript/Guide/Control_flow_and_error_handling", "Web/JavaScript/Guide/Functions")}}
Loops offer a quick and easy way to do something repeatedly. This chapter of the JavaScript Guide introduces the different iteration statements available to JavaScript.
You can think of a loop as a computerized version of the game where you tell someone to take X steps in one direction, then Y steps in another. For example, the idea "Go five steps to the east" could be expressed this way as a loop:
for (let step = 0; step < 5; step++) {
// Runs 5 times, with values of step 0 through 4.
console.log("Walking east one step");
}
There are many different kinds of loops, but they all essentially do the same thing: they repeat an action some number of times. (Note that it's possible that number could be zero!)
The various loop mechanisms offer different ways to determine the start and end points of the loop. There are various situations that are more easily served by one type of loop over the others.
The statements for loops provided in JavaScript are:
- for statement
- do...while statement
- while statement
- labeled statement
- break statement
- continue statement
- for...in statement
- for...of statement
for statement
A {{jsxref("Statements/for", "for")}} loop repeats until a specified condition evaluates to false. The JavaScript for loop is similar to the Java and C for loop.
A for statement looks as follows:
for (initialization; condition; afterthought)
statement
When a for loop executes, the following occurs:
- The initializing expression
initialization, if any, is executed. This expression usually initializes one or more loop counters, but the syntax allows an expression of any degree of complexity. This expression can also declare variables. - The
conditionexpression is evaluated. If the value ofconditionis true, the loop statements execute. Otherwise, theforloop terminates. (If theconditionexpression is omitted entirely, the condition is assumed to be true.) - The
statementexecutes. To execute multiple statements, use a block statement ({ }) to group those statements. - If present, the update expression
afterthoughtis executed. - Control returns to Step 2.
Example
In the example below, the function contains a for statement that counts
the number of selected options in a scrolling list (a <select>
element that allows multiple selections).
HTML
<form name="selectForm">
<label for="musicTypes"
>Choose some music types, then click the button below:</label
>
<select id="musicTypes" name="musicTypes" multiple>
<option selected>R&B</option>
<option>Jazz</option>
<option>Blues</option>
<option>New Age</option>
<option>Classical</option>
<option>Opera</option>
</select>
<button id="btn" type="button">How many are selected?</button>
</form>
JavaScript
Here, the for statement declares the variable i and initializes it to 0. It checks that i is less than the number of options in the <select> element, performs the succeeding if statement, and increments i by 1 after each pass through the loop.
function countSelected(selectObject) {
let numberSelected = 0;
for (let i = 0; i < selectObject.options.length; i++) {
if (selectObject.options[i].selected) {
numberSelected++;
}
}
return numberSelected;
}
const btn = document.getElementById("btn");
btn.addEventListener("click", () => {
const musicTypes = document.selectForm.musicTypes;
console.log(`You have selected ${countSelected(musicTypes)} option(s).`);
});
do...while statement
The {{jsxref("Statements/do...while", "do...while")}} statement repeats until a specified condition evaluates to false.
A do...while statement looks as follows:
do
statement
while (condition);
statement is always executed once before the condition is
checked. (To execute multiple statements, use a block statement ({ })
to group those statements.)
If condition is true, the statement executes again. At the
end of every execution, the condition is checked. When the condition is
false, execution stops, and control passes to the statement following
do...while.
Example
In the following example, the do loop iterates at least once and
reiterates until i is no longer less than 5.
let i = 0;
do {
i += 1;
console.log(i);
} while (i < 5);
while statement
A {{jsxref("Statements/while", "while")}} statement executes its statements as long as a
specified condition evaluates to true. A while statement looks
as follows:
while (condition)
statement
If the condition becomes false,
statement within the loop stops executing and control passes to the
statement following the loop.
The condition test occurs before statement in the loop is
executed. If the condition returns true, statement is executed
and the condition is tested again. If the condition returns
false, execution stops, and control is passed to the statement following
while.
To execute multiple statements, use a block statement ({ }) to group
those statements.
Example 1
The following while loop iterates as long as n is
less than 3:
let n = 0;
let x = 0;
while (n < 3) {
n++;
x += n;
}
With each iteration, the loop increments n and adds that value to
x. Therefore, x and n take on the following
values:
- After the first pass:
n=1andx=1 - After the second pass:
n=2andx=3 - After the third pass:
n=3andx=6
After completing the third pass, the condition n < 3 is no longer
true, so the loop terminates.
Example 2
Avoid infinite loops. Make sure the condition in a loop eventually becomes
false—otherwise, the loop will never terminate! The statements in the
following while loop execute forever because the condition never becomes
false:
// Infinite loops are bad!
while (true) {
console.log("Hello, world!");
}
labeled statement
A {{jsxref("Statements/label", "label")}} provides a statement with an identifier that
lets you refer to it elsewhere in your program. For example, you can use a label to
identify a loop, and then use the break or continue statements
to indicate whether a program should interrupt the loop or continue its execution.
The syntax of the labeled statement looks like the following:
label:
statement
The value of label may be any JavaScript identifier that is not a
reserved word. The statement that you identify with a label may be
any statement. For examples of using labeled statements, see the examples of break and continue below.
break statement
Use the {{jsxref("Statements/break", "break")}} statement to terminate a loop,
switch, or in conjunction with a labeled statement.
- When you use
breakwithout a label, it terminates the innermost enclosingwhile,do-while,for, orswitchimmediately and transfers control to the following statement. - When you use
breakwith a label, it terminates the specified labeled statement.
The syntax of the break statement looks like this:
break;
break label;
- The first form of the syntax terminates the innermost enclosing loop or
switch. - The second form of the syntax terminates the specified enclosing labeled statement.
Example 1
The following example iterates through the elements in an array until it finds the
index of an element whose value is theValue:
for (let i = 0; i < a.length; i++) {
if (a[i] === theValue) {
break;
}
}
Example 2: Breaking to a label
let x = 0;
let z = 0;
labelCancelLoops: while (true) {
console.log("Outer loops:", x);
x += 1;
z = 1;
while (true) {
console.log("Inner loops:", z);
z += 1;
if (z === 10 && x === 10) {
break labelCancelLoops;
} else if (z === 10) {
break;
}
}
}
continue statement
The {{jsxref("Statements/continue", "continue")}} statement can be used to restart a
while, do-while, for, or label
statement.
- When you use
continuewithout a label, it terminates the current iteration of the innermost enclosingwhile,do-while, orforstatement and continues execution of the loop with the next iteration. In contrast to thebreakstatement,continuedoes not terminate the execution of the loop entirely. In awhileloop, it jumps back to the condition. In aforloop, it jumps to theincrement-expression. - When you use
continuewith a label, it applies to the looping statement identified with that label.
The syntax of the continue statement looks like the following:
continue;
continue label;
Example 1
The following example shows a while loop with a continue
statement that executes when the value of i is 3. Thus,
n takes on the values 1, 3, 7, and
12.
let i = 0;
let n = 0;
while (i < 5) {
i++;
if (i === 3) {
continue;
}
n += i;
console.log(n);
}
// Logs:
// 1 3 7 12
If you comment out the continue;, the loop would run till the end and you would see 1,3,6,10,15.
Example 2
A statement labeled checkIandJ contains a statement labeled
checkJ. If continue is encountered, the program
terminates the current iteration of checkJ and begins the next
iteration. Each time continue is encountered, checkJ
reiterates until its condition returns false. When false is
returned, the remainder of the checkIandJ statement is completed,
and checkIandJ reiterates until its condition returns
false. When false is returned, the program continues at the
statement following checkIandJ.
If continue had a label of checkIandJ, the program
would continue at the top of the checkIandJ statement.
let i = 0;
let j = 10;
checkIandJ: while (i < 4) {
console.log(i);
i += 1;
checkJ: while (j > 4) {
console.log(j);
j -= 1;
if (j % 2 === 0) {
continue;
}
console.log(j, "is odd.");
}
console.log("i =", i);
console.log("j =", j);
}
for...in statement
The {{jsxref("Statements/for...in", "for...in")}} statement iterates a specified
variable over all the enumerable properties of an object. For each distinct property,
JavaScript executes the specified statements. A for...in statement looks as
follows:
for (variable in object)
statement
Example
The following function takes as its argument an object and the object's name. It then iterates over all the object's properties and returns a string that lists the property names and their values.
function dumpProps(obj, objName) {
let result = "";
for (const i in obj) {
result += `${objName}.${i} = ${obj[i]}<br>`;
}
result += "<hr>";
return result;
}
For an object car with properties make and model, result would be:
car.make = Ford
car.model = Mustang
Arrays
Although it may be tempting to use this as a way to iterate over {{jsxref("Array")}}
elements, the for...in statement will return the name of your user-defined
properties in addition to the numeric indexes.
Therefore, it is better to use a traditional {{jsxref("Statements/for", "for")}} loop
with a numeric index when iterating over arrays, because the for...in
statement iterates over user-defined properties in addition to the array elements, if
you modify the Array object (such as adding custom properties or methods).
for...of statement
The {{jsxref("Statements/for...of", "for...of")}} statement creates a loop Iterating over iterable objects (including {{jsxref("Array")}}, {{jsxref("Map")}}, {{jsxref("Set")}}, {{jsxref("Functions/arguments", "arguments")}} object and so on), invoking a custom iteration hook with statements to be executed for the value of each distinct property.
for (variable of iterable)
statement
The following example shows the difference between a for...of loop and a
{{jsxref("Statements/for...in", "for...in")}} loop. While for...in iterates
over property names, for...of iterates over property values:
const arr = [3, 5, 7];
arr.foo = "hello";
for (const i in arr) {
console.log(i);
}
// "0" "1" "2" "foo"
for (const i of arr) {
console.log(i);
}
// Logs: 3 5 7
The for...of and for...in statements can also be used with destructuring. For example, you can simultaneously loop over the keys and values of an object using {{jsxref("Object.entries()")}}.
const obj = { foo: 1, bar: 2 };
for (const [key, val] of Object.entries(obj)) {
console.log(key, val);
}
// "foo" 1
// "bar" 2
{{PreviousNext("Web/JavaScript/Guide/Control_flow_and_error_handling", "Web/JavaScript/Guide/Functions")}}
{{PreviousNext("Web/JavaScript/Guide/Keyed_collections", "Web/JavaScript/Guide/Using_classes")}}
JavaScript is designed on an object-based paradigm. An object is a collection of properties, and a property is an association between a name (or key) and a value. A property's value can be a function, in which case the property is known as a method.
Objects in JavaScript, just as in many other programming languages, can be compared to objects in real life. In JavaScript, an object is a standalone entity, with properties and type. Compare it with a cup, for example. A cup is an object with properties. A cup has a color, a design, weight, a material it is made of, etc. In the same way, JavaScript objects can have properties, which define their characteristics.
In addition to objects that are predefined in the browser, you can define your own objects. This chapter describes how to use objects, properties, and methods, and how to create your own objects.
Creating new objects
You can create an object using an object initializer. Alternatively, you can first create a constructor function and then instantiate an object by invoking that function with the new operator.
Using object initializers
Object initializers are also called object literals. "Object initializer" is consistent with the terminology used by C++.
The syntax for an object using an object initializer is:
const obj = {
property1: value1, // property name may be an identifier
2: value2, // or a number
"property n": value3, // or a string
};
Each property name before the colon is either an identifier, a number literal, or a string literal, and each valueN is an expression whose value is assigned to the property name. The property name can also be an expression; computed keys need to be wrapped in square brackets. The object initializer reference contains a more detailed explanation of the syntax.
In this example, the newly created object is assigned to a variable obj — this is optional. If you do not need to refer to this object elsewhere, you do not need to assign it to a variable. (Note that you may need to wrap the object literal in parentheses if the object appears where a statement is expected, so as not to have the literal be confused with a block statement.)
Object initializers are expressions, and each object initializer results in a new object being created whenever the statement in which it appears is executed. Identical object initializers create distinct objects that do not compare to each other as equal.
The following statement creates an object and assigns it to the variable x if and only if the expression cond is true:
let x;
if (cond) {
x = { greeting: "hi there" };
}
The following example creates myHonda with three properties. Note that the engine property is also an object with its own properties.
const myHonda = {
color: "red",
wheels: 4,
engine: { cylinders: 4, size: 2.2 },
};
Objects created with initializers are called plain objects, because they are instances of {{jsxref("Object")}}, but not any other object type. Some object types have special initializer syntaxes — for example, array initializers and regex literals.
Using a constructor function
Alternatively, you can create an object with these two steps:
- Define the object type by writing a constructor function. There is a strong convention, with good reason, to use a capital initial letter.
- Create an instance of the object with
new.
To define an object type, create a function for the object type that specifies its name, properties, and methods. For example, suppose you want to create an object type for cars. You want this type of object to be called Car, and you want it to have properties for make, model, and year. To do this, you would write the following function:
function Car(make, model, year) {
this.make = make;
this.model = model;
this.year = year;
}
Notice the use of this to assign values to the object's properties based on the values passed to the function.
Now you can create an object called myCar as follows:
const myCar = new Car("Eagle", "Talon TSi", 1993);
This statement creates myCar and assigns it the specified values for its properties. Then the value of myCar.make is the string "Eagle", myCar.model is the string "Talon TSi", myCar.year is the integer 1993, and so on. The order of arguments and parameters should be the same.
You can create any number of Car objects by calls to new. For example,
const randCar = new Car("Nissan", "300ZX", 1992);
const kenCar = new Car("Mazda", "Miata", 1990);
An object can have a property that is itself another object. For example, suppose you define an object called Person as follows:
function Person(name, age, sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
and then instantiate two new Person objects as follows:
const rand = new Person("Rand McKinnon", 33, "M");
const ken = new Person("Ken Jones", 39, "M");
Then, you can rewrite the definition of Car to include an owner property that takes a Person object, as follows:
function Car(make, model, year, owner) {
this.make = make;
this.model = model;
this.year = year;
this.owner = owner;
}
To instantiate the new objects, you then use the following:
const car1 = new Car("Eagle", "Talon TSi", 1993, rand);
const car2 = new Car("Nissan", "300ZX", 1992, ken);
Notice that instead of passing a literal string or integer value when creating the new objects, the above statements pass the objects rand and ken as the arguments for the owners. Then if you want to find out the name of the owner of car2, you can access the following property:
car2.owner.name;
You can always add a property to a previously defined object. For example, the statement
car1.color = "black";
adds a property color to car1, and assigns it a value of "black". However, this does not affect any other objects. To add the new property to all objects of the same type, you have to add the property to the definition of the Car object type.
You can also use the class syntax instead of the function syntax to define a constructor function. For more information, see the class guide.
Using the Object.create() method
Objects can also be created using the {{jsxref("Object.create()")}} method. This method can be very useful, because it allows you to choose the prototype object for the object you want to create, without having to define a constructor function.
// Animal properties and method encapsulation
const animalProto = {
type: "Invertebrates", // Default value of properties
displayType() {
// Method which will display the type of animal
console.log(this.type);
},
};
// Create a new animal type called `animal`
const animal = Object.create(animalProto);
animal.displayType(); // Logs: Invertebrates
// Create a new animal type called fish
const fish = Object.create(animalProto);
fish.type = "Fishes";
fish.displayType(); // Logs: Fishes
Objects and properties
A JavaScript object has properties associated with it. Object properties are basically the same as variables, except that they are associated with objects, not scopes. The properties of an object define the characteristics of the object.
For example, this example creates an object named myCar, with properties named make, model, and year, with their values set to "Ford", "Mustang", and 1969:
const myCar = {
make: "Ford",
model: "Mustang",
year: 1969,
};
Like JavaScript variables, property names are case sensitive. Property names can only be strings or Symbols — all keys are converted to strings unless they are Symbols. Array indices are, in fact, properties with string keys that contain integers.
Accessing properties
You can access a property of an object by its property name. Property accessors come in two syntaxes: dot notation and bracket notation. For example, you could access the properties of the myCar object as follows:
// Dot notation
myCar.make = "Ford";
myCar.model = "Mustang";
myCar.year = 1969;
// Bracket notation
myCar["make"] = "Ford";
myCar["model"] = "Mustang";
myCar["year"] = 1969;
An object property name can be any JavaScript string or symbol, including an empty string. However, you cannot use dot notation to access a property whose name is not a valid JavaScript identifier. For example, a property name that has a space or a hyphen, that starts with a number, or that is held inside a variable can only be accessed using the bracket notation. This notation is also very useful when property names are to be dynamically determined, i.e., not determinable until runtime. Examples are as follows:
const myObj = {};
const str = "myString";
const rand = Math.random();
const anotherObj = {};
// Create additional properties on myObj
myObj.type = "Dot syntax for a key named type";
myObj["date created"] = "This key has a space";
myObj[str] = "This key is in variable str";
myObj[rand] = "A random number is the key here";
myObj[anotherObj] = "This key is object anotherObj";
myObj[""] = "This key is an empty string";
console.log(myObj);
// {
// type: 'Dot syntax for a key named type',
// 'date created': 'This key has a space',
// myString: 'This key is in variable str',
// '0.6398914448618778': 'A random number is the key here',
// '[object Object]': 'This key is object anotherObj',
// '': 'This key is an empty string'
// }
console.log(myObj.myString); // 'This key is in variable str'
In the above code, the key anotherObj is an object, which is neither a string nor a symbol. When it is added to the myObj, JavaScript calls the {{jsxref("Object/toString", "toString()")}} method of anotherObj, and use the resulting string as the new key.
You can also access properties with a string value stored in a variable. The variable must be passed in bracket notation. In the example above, the variable str held "myString" and it is "myString" that is the property name. Therefore, myObj.str will return as undefined.
str = "myString";
myObj[str] = "This key is in variable str";
console.log(myObj.str); // undefined
console.log(myObj[str]); // 'This key is in variable str'
console.log(myObj.myString); // 'This key is in variable str'
This allows accessing any property as determined at runtime:
let propertyName = "make";
myCar[propertyName] = "Ford";
// access different properties by changing the contents of the variable
propertyName = "model";
myCar[propertyName] = "Mustang";
console.log(myCar); // { make: 'Ford', model: 'Mustang' }
However, beware of using square brackets to access properties whose names are given by external input. This may make your code susceptible to object injection attacks.
Nonexistent properties of an object have the value {{jsxref("undefined")}} (and not null).
myCar.nonexistentProperty; // undefined
Enumerating properties
There are three native ways to list/traverse object properties:
for...inloops. This method traverses all of the enumerable string properties of an object as well as its prototype chain.- {{jsxref("Object.keys()")}}. This method returns an array with only the enumerable own string property names ("keys") in the object
myObj, but not those in the prototype chain. - {{jsxref("Object.getOwnPropertyNames()")}}. This method returns an array containing all the own string property names in the object
myObj, regardless of if they are enumerable or not.
You can use the bracket notation with for...in to iterate over all the enumerable properties of an object. To illustrate how this works, the following function displays the properties of the object when you pass the object and the object's name as arguments to the function:
function showProps(obj, objName) {
let result = "";
for (const i in obj) {
// Object.hasOwn() is used to exclude properties from the object's
// prototype chain and only show "own properties"
if (Object.hasOwn(obj, i)) {
result += `${objName}.${i} = ${obj[i]}\n`;
}
}
console.log(result);
}
The term "own property" refers to the properties of the object, but excluding those of the prototype chain. So, the function call showProps(myCar, 'myCar') would print the following:
myCar.make = Ford
myCar.model = Mustang
myCar.year = 1969
The above is equivalent to:
function showProps(obj, objName) {
let result = "";
Object.keys(obj).forEach((i) => {
result += `${objName}.${i} = ${obj[i]}\n`;
});
console.log(result);
}
There is no native way to list all inherited properties, including non-enumerable ones. However, this can be achieved with the following function:
function listAllProperties(myObj) {
let objectToInspect = myObj;
let result = [];
while (objectToInspect !== null) {
result = result.concat(Object.getOwnPropertyNames(objectToInspect));
objectToInspect = Object.getPrototypeOf(objectToInspect);
}
return result;
}
For more information, see Enumerability and ownership of properties.
Deleting properties
You can remove a non-inherited property using the delete operator. The following code shows how to remove a property.
// Creates a new object, myObj, with two properties, a and b.
const myObj = { a: 5, b: 12 };
// Removes the a property, leaving myObj with only the b property.
delete myObj.a;
console.log("a" in myObj); // false
Inheritance
All objects in JavaScript inherit from at least one other object. The object being inherited from is known as the prototype, and the inherited properties can be found in the prototype object of the constructor. See Inheritance and the prototype chain for more information.
Defining properties for all objects of one type
You can add a property to all objects created through a certain constructor using the prototype property. This defines a property that is shared by all objects of the specified type, rather than by just one instance of the object. The following code adds a color property to all objects of type Car, and then reads the property's value from an instance car1.
Car.prototype.color = "red";
console.log(car1.color); // "red"
Defining methods
A method is a function associated with an object, or, put differently, a method is a property of an object that is a function. Methods are defined the way normal functions are defined, except that they have to be assigned as the property of an object. See also method definitions for more details. An example is:
objectName.methodName = functionName;
const myObj = {
myMethod: function (params) {
// do something
},
// this works too!
myOtherMethod(params) {
// do something else
},
};
where objectName is an existing object, methodName is the name you are assigning to the method, and functionName is the name of the function.
You can then call the method in the context of the object as follows:
objectName.methodName(params);
Methods are typically defined on the prototype object of the constructor, so that all objects of the same type share the same method. For example, you can define a function that formats and displays the properties of the previously-defined Car objects.
Car.prototype.displayCar = function () {
const result = `A Beautiful ${this.year} ${this.make} ${this.model}`;
console.log(result);
};
Notice the use of this to refer to the object to which the method belongs. Then you can call the displayCar method for each of the objects as follows:
car1.displayCar();
car2.displayCar();
Using this for object references
JavaScript has a special keyword, this, that you can use within a method to refer to the current object. For example, suppose you have 2 objects, manager and intern. Each object has its own name, age and job. In the function sayHi(), notice the use of this.name. When added to the 2 objects, the same function will print the message with the name of the respective object it's attached to.
const manager = {
name: "Karina",
age: 27,
job: "Software Engineer",
};
const intern = {
name: "Tyrone",
age: 21,
job: "Software Engineer Intern",
};
function sayHi() {
console.log(`Hello, my name is ${this.name}`);
}
// Add sayHi function to both objects
manager.sayHi = sayHi;
intern.sayHi = sayHi;
manager.sayHi(); // Hello, my name is Karina
intern.sayHi(); // Hello, my name is Tyrone
this is a "hidden parameter" of a function call that's passed in by specifying the object before the function that was called. For example, in manager.sayHi(), this is the manager object, because manager comes before the function sayHi(). If you access the same function from another object, this will change as well. If you use other methods to call the function, like {{jsxref("Function.prototype.call()")}} or {{jsxref("Reflect.apply()")}}, you can explicitly pass the value of this as an argument.
Defining getters and setters
A getter is a function associated with a property that gets the value of a specific property. A setter is a function associated with a property that sets the value of a specific property. Together, they can indirectly represent the value of a property.
Getters and setters can be either
- defined within object initializers, or
- added later to any existing object.
Within object initializers, getters and setters are defined like regular methods, but prefixed with the keywords get or set. The getter method must not expect a parameter, while the setter method expects exactly one parameter (the new value to set). For instance:
const myObj = {
a: 7,
get b() {
return this.a + 1;
},
set c(x) {
this.a = x / 2;
},
};
console.log(myObj.a); // 7
console.log(myObj.b); // 8, returned from the get b() method
myObj.c = 50; // Calls the set c(x) method
console.log(myObj.a); // 25
The myObj object's properties are:
myObj.a— a numbermyObj.b— a getter that returnsmyObj.aplus 1myObj.c— a setter that sets the value ofmyObj.ato half of the valuemyObj.cis being set to
Getters and setters can also be added to an object at any time after creation using the {{jsxref("Object.defineProperties()")}} method. This method's first parameter is the object on which you want to define the getter or setter. The second parameter is an object whose property names are the getter or setter names, and whose property values are objects for defining the getter or setter functions. Here's an example that defines the same getter and setter used in the previous example:
const myObj = { a: 0 };
Object.defineProperties(myObj, {
b: {
get() {
return this.a + 1;
},
},
c: {
set(x) {
this.a = x / 2;
},
},
});
myObj.c = 10; // Runs the setter, which assigns 10 / 2 (5) to the 'a' property
console.log(myObj.b); // Runs the getter, which yields a + 1 or 6
Which of the two forms to choose depends on your programming style and task at hand. If you can change the definition of the original object, you will probably define getters and setters through the original initializer. This form is more compact and natural. However, if you need to add getters and setters later — maybe because you did not write the particular object — then the second form is the only possible form. The second form better represents the dynamic nature of JavaScript, but it can make the code hard to read and understand.
Comparing objects
In JavaScript, objects are a reference type. Two distinct objects are never equal, even if they have the same properties. Only comparing the same object reference with itself yields true.
// Two variables, two distinct objects with the same properties
const fruit = { name: "apple" };
const anotherFruit = { name: "apple" };
fruit == anotherFruit; // return false
fruit === anotherFruit; // return false
// Two variables, a single object
const fruit = { name: "apple" };
const anotherFruit = fruit; // Assign fruit object reference to anotherFruit
// Here fruit and anotherFruit are pointing to same object
fruit == anotherFruit; // return true
fruit === anotherFruit; // return true
fruit.name = "grape";
console.log(anotherFruit); // { name: "grape" }; not { name: "apple" }
For more information about comparison operators, see equality operators.
See also
{{PreviousNext("Web/JavaScript/Guide/Regular_expressions", "Web/JavaScript/Guide/Using_classes")}}
{{PreviousNext("Web/JavaScript/Guide/Using_classes", "Web/JavaScript/Guide/Typed_arrays")}}
A {{jsxref("Promise")}} is an object representing the eventual completion or failure of an asynchronous operation. Since most people are consumers of already-created promises, this guide will explain consumption of returned promises before explaining how to create them.
Essentially, a promise is a returned object to which you attach callbacks, instead of passing callbacks into a function. Imagine a function, createAudioFileAsync(), which asynchronously generates a sound file given a configuration record and two callback functions: one called if the audio file is successfully created, and the other called if an error occurs.
Here's some code that uses createAudioFileAsync():
function successCallback(result) {
console.log(`Audio file ready at URL: ${result}`);
}
function failureCallback(error) {
console.error(`Error generating audio file: ${error}`);
}
createAudioFileAsync(audioSettings, successCallback, failureCallback);
If createAudioFileAsync() were rewritten to return a promise, you would attach your callbacks to it instead:
createAudioFileAsync(audioSettings).then(successCallback, failureCallback);
This convention has several advantages. We will explore each one.
Chaining
A common need is to execute two or more asynchronous operations back to back, where each subsequent operation starts when the previous operation succeeds, with the result from the previous step. In the old days, doing several asynchronous operations in a row would lead to the classic callback hell:
doSomething(function (result) {
doSomethingElse(result, function (newResult) {
doThirdThing(newResult, function (finalResult) {
console.log(`Got the final result: ${finalResult}`);
}, failureCallback);
}, failureCallback);
}, failureCallback);
With promises, we accomplish this by creating a promise chain. The API design of promises makes this great, because callbacks are attached to the returned promise object, instead of being passed into a function.
Here's the magic: the then() function returns a new promise, different from the original:
const promise = doSomething();
const promise2 = promise.then(successCallback, failureCallback);
This second promise (promise2) represents the completion not just of doSomething(), but also of the successCallback or failureCallback you passed in — which can be other asynchronous functions returning a promise. When that's the case, any callbacks added to promise2 get queued behind the promise returned by either successCallback or failureCallback.
Note
If you want a working example to play with, you can use the following template to create any function returning a promise:
function doSomething() { return new Promise((resolve) => { setTimeout(() => { // Other things to do before completion of the promise console.log("Did something"); // The fulfillment value of the promise resolve("https://example.com/"); }, 200); }); }The implementation is discussed in the Creating a Promise around an old callback API section below.
With this pattern, you can create longer chains of processing, where each promise represents the completion of one asynchronous step in the chain. In addition, the arguments to then are optional, and catch(failureCallback) is short for then(null, failureCallback) — so if your error handling code is the same for all steps, you can attach it to the end of the chain:
doSomething()
.then(function (result) {
return doSomethingElse(result);
})
.then(function (newResult) {
return doThirdThing(newResult);
})
.then(function (finalResult) {
console.log(`Got the final result: ${finalResult}`);
})
.catch(failureCallback);
You might see this expressed with arrow functions instead:
doSomething()
.then((result) => doSomethingElse(result))
.then((newResult) => doThirdThing(newResult))
.then((finalResult) => {
console.log(`Got the final result: ${finalResult}`);
})
.catch(failureCallback);
Note
Arrow function expressions can have an implicit return; so,
() => xis short for() => { return x; }.
doSomethingElse and doThirdThing can return any value — if they return promises, that promise is first waited until it settles, and the next callback receives the fulfillment value, not the promise itself. It is important to always return promises from then callbacks, even if the promise always resolves to undefined. If the previous handler started a promise but did not return it, there's no way to track its settlement anymore, and the promise is said to be "floating".
doSomething()
.then((url) => {
// Missing `return` keyword in front of fetch(url).
fetch(url);
})
.then((result) => {
// result is undefined, because nothing is returned from the previous
// handler. There's no way to know the return value of the fetch()
// call anymore, or whether it succeeded at all.
});
By returning the result of the fetch call (which is a promise), we can both track its completion and receive its value when it completes.
doSomething()
.then((url) => {
// `return` keyword added
return fetch(url);
})
.then((result) => {
// result is a Response object
});
Floating promises could be worse if you have race conditions — if the promise from the last handler is not returned, the next then handler will be called early, and any value it reads may be incomplete.
const listOfIngredients = [];
doSomething()
.then((url) => {
// Missing `return` keyword in front of fetch(url).
fetch(url)
.then((res) => res.json())
.then((data) => {
listOfIngredients.push(data);
});
})
.then(() => {
console.log(listOfIngredients);
// listOfIngredients will always be [], because the fetch request hasn't completed yet.
});
Therefore, as a rule of thumb, whenever your operation encounters a promise, return it and defer its handling to the next then handler.
const listOfIngredients = [];
doSomething()
.then((url) => {
// `return` keyword now included in front of fetch call.
return fetch(url)
.then((res) => res.json())
.then((data) => {
listOfIngredients.push(data);
});
})
.then(() => {
console.log(listOfIngredients);
// listOfIngredients will now contain data from fetch call.
});
Even better, you can flatten the nested chain into a single chain, which is simpler and makes error handling easier. The details are discussed in the Nesting section below.
doSomething()
.then((url) => fetch(url))
.then((res) => res.json())
.then((data) => {
listOfIngredients.push(data);
})
.then(() => {
console.log(listOfIngredients);
});
Using async/await can help you write code that's more intuitive and resembles synchronous code. Below is the same example using async/await:
async function logIngredients() {
const url = await doSomething();
const res = await fetch(url);
const data = await res.json();
listOfIngredients.push(data);
console.log(listOfIngredients);
}
Note how the code looks exactly like synchronous code, except for the await keywords in front of promises. One of the only tradeoffs is that it may be easy to forget the await keyword, which can only be fixed when there's a type mismatch (e.g., trying to use a promise as a value).
async/await builds on promises — for example, doSomething() is the same function as before, so there's minimal refactoring needed to change from promises to async/await. You can read more about the async/await syntax in the async functions and await references.
Note
async/awaithas the same concurrency semantics as normal promise chains.awaitwithin one async function does not stop the entire program, only the parts that depend on its value, so other async jobs can still run while theawaitis pending.
Error handling
You might recall seeing failureCallback three times in the pyramid of doom earlier, compared to only once at the end of the promise chain:
doSomething()
.then((result) => doSomethingElse(result))
.then((newResult) => doThirdThing(newResult))
.then((finalResult) => console.log(`Got the final result: ${finalResult}`))
.catch(failureCallback);
If there's an exception, the browser will look down the chain for .catch() handlers or onRejected. This is very much modeled after how synchronous code works:
try {
const result = syncDoSomething();
const newResult = syncDoSomethingElse(result);
const finalResult = syncDoThirdThing(newResult);
console.log(`Got the final result: ${finalResult}`);
} catch (error) {
failureCallback(error);
}
This symmetry with asynchronous code culminates in the async/await syntax:
async function foo() {
try {
const result = await doSomething();
const newResult = await doSomethingElse(result);
const finalResult = await doThirdThing(newResult);
console.log(`Got the final result: ${finalResult}`);
} catch (error) {
failureCallback(error);
}
}
Promises solve a fundamental flaw with the callback pyramid of doom, by catching all errors, even thrown exceptions and programming errors. This is essential for functional composition of asynchronous operations. All errors are now handled by the catch() method at the end of the chain, and you should almost never need to use try/catch without using async/await.
Nesting
In the examples above involving listOfIngredients, the first one has one promise chain nested in the return value of another then() handler, while the second one uses an entirely flat chain. Simple promise chains are best kept flat without nesting, as nesting can be a result of careless composition.
Nesting is a control structure to limit the scope of catch statements. Specifically, a nested catch only catches failures in its scope and below, not errors higher up in the chain outside the nested scope. When used correctly, this gives greater precision in error recovery:
doSomethingCritical()
.then((result) =>
doSomethingOptional(result)
.then((optionalResult) => doSomethingExtraNice(optionalResult))
.catch((e) => {}),
) // Ignore if optional stuff fails; proceed.
.then(() => moreCriticalStuff())
.catch((e) => console.error(`Critical failure: ${e.message}`));
Note that the optional steps here are nested — with the nesting caused not by the indentation, but by the placement of the outer ( and ) parentheses around the steps.
The inner error-silencing catch handler only catches failures from doSomethingOptional() and doSomethingExtraNice(), after which the code resumes with moreCriticalStuff(). Importantly, if doSomethingCritical() fails, its error is caught by the final (outer) catch only, and does not get swallowed by the inner catch handler.
In async/await, this code looks like:
async function main() {
try {
const result = await doSomethingCritical();
try {
const optionalResult = await doSomethingOptional(result);
await doSomethingExtraNice(optionalResult);
} catch (e) {
// Ignore failures in optional steps and proceed.
}
await moreCriticalStuff();
} catch (e) {
console.error(`Critical failure: ${e.message}`);
}
}
Note
If you don't have sophisticated error handling, you very likely don't need nested
thenhandlers. Instead, use a flat chain and put the error handling logic at the end.
Chaining after a catch
It's possible to chain after a failure, i.e., a catch, which is useful to accomplish new actions even after an action failed in the chain. Read the following example:
doSomething()
.then(() => {
throw new Error("Something failed");
console.log("Do this");
})
.catch(() => {
console.error("Do that");
})
.then(() => {
console.log("Do this, no matter what happened before");
});
This will output the following text:
Do that
Do this, no matter what happened before
Note
The text "Do this" is not displayed because the "Something failed" error caused a rejection.
In async/await, this code looks like:
async function main() {
try {
await doSomething();
throw new Error("Something failed");
console.log("Do this");
} catch (e) {
console.error("Do that");
}
console.log("Do this, no matter what happened before");
}
Promise rejection events
If a promise rejection event is not handled by any handler, it bubbles to the top of the call stack, and the host needs to surface it. On the web, whenever a promise is rejected, one of two events is sent to the global scope (generally, this is either the window or, if being used in a web worker, it's the Worker or other worker-based interface). The two events are:
unhandledrejection- : Sent when a promise is rejected but there is no rejection handler available.
rejectionhandled- : Sent when a handler is attached to a rejected promise that has already caused an
unhandledrejectionevent.
- : Sent when a handler is attached to a rejected promise that has already caused an
In both cases, the event (of type PromiseRejectionEvent) has as members a promise property indicating the promise that was rejected, and a reason property that provides the reason given for the promise to be rejected.
These make it possible to offer fallback error handling for promises, as well as to help debug issues with your promise management. These handlers are global per context, so all errors will go to the same event handlers, regardless of source.
In Node.js, handling promise rejection is slightly different. You capture unhandled rejections by adding a handler for the Node.js unhandledRejection event (notice the difference in capitalization of the name), like this:
process.on("unhandledRejection", (reason, promise) => {
// Add code here to examine the "promise" and "reason" values
});
For Node.js, to prevent the error from being logged to the console (the default action that would otherwise occur), adding that process.on() listener is all that's necessary; there's no need for an equivalent of the browser runtime's preventDefault() method.
However, if you add that process.on listener but don't also have code within it to handle rejected promises, they will just be dropped on the floor and silently ignored. So ideally, you should add code within that listener to examine each rejected promise and make sure it was not caused by an actual code bug.
Composition
There are four composition tools for running asynchronous operations concurrently: {{jsxref("Promise.all()")}}, {{jsxref("Promise.allSettled()")}}, {{jsxref("Promise.any()")}}, and {{jsxref("Promise.race()")}}.
We can start operations at the same time and wait for them all to finish like this:
Promise.all([func1(), func2(), func3()]).then(([result1, result2, result3]) => {
// use result1, result2 and result3
});
If one of the promises in the array rejects, Promise.all() immediately rejects the returned promise. The other operations continue to run, but their outcomes are not available via the return value of Promise.all(). This may cause unexpected state or behavior. {{jsxref("Promise.allSettled()")}} is another composition tool that ensures all operations are complete before resolving.
These methods all run promises concurrently — a sequence of promises are started simultaneously and do not wait for each other. Sequential composition is possible using some clever JavaScript:
[func1, func2, func3]
.reduce((p, f) => p.then(f), Promise.resolve())
.then((result3) => {
/* use result3 */
});
In this example, we reduce an array of asynchronous functions down to a promise chain. The code above is equivalent to:
Promise.resolve()
.then(func1)
.then(func2)
.then(func3)
.then((result3) => {
/* use result3 */
});
This can be made into a reusable compose function, which is common in functional programming:
const applyAsync = (acc, val) => acc.then(val);
const composeAsync =
(...funcs) =>
(x) =>
funcs.reduce(applyAsync, Promise.resolve(x));
The composeAsync() function accepts any number of functions as arguments and returns a new function that accepts an initial value to be passed through the composition pipeline:
const transformData = composeAsync(func1, func2, func3);
const result3 = transformData(data);
Sequential composition can also be done more succinctly with async/await:
let result;
for (const f of [func1, func2, func3]) {
result = await f(result);
}
/* use last result (i.e. result3) */
However, before you compose promises sequentially, consider if it's really necessary — it's always better to run promises concurrently so that they don't unnecessarily block each other unless one promise's execution depends on another's result.
Cancellation
Promise itself has no first-class protocol for cancellation, but you may be able to directly cancel the underlying asynchronous operation, typically using AbortController.
Creating a Promise around an old callback API
A {{jsxref("Promise")}} can be created from scratch using its constructor. This should be needed only to wrap old APIs.
In an ideal world, all asynchronous functions would already return promises. Unfortunately, some APIs still expect success and/or failure callbacks to be passed in the old way. The most obvious example is the {{domxref("Window.setTimeout", "setTimeout()")}} function:
setTimeout(() => saySomething("10 seconds passed"), 10 * 1000);
Mixing old-style callbacks and promises is problematic. If saySomething() fails or contains a programming error, nothing catches it. This is intrinsic to the design of setTimeout().
Luckily we can wrap setTimeout() in a promise. The best practice is to wrap the callback-accepting functions at the lowest possible level, and then never call them directly again:
const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
wait(10 * 1000)
.then(() => saySomething("10 seconds"))
.catch(failureCallback);
The promise constructor takes an executor function that lets us resolve or reject a promise manually. Since setTimeout() doesn't really fail, we left out reject in this case. For more information on how the executor function works, see the Promise() reference.
Timing
Lastly, we will look into the more technical details, about when the registered callbacks get called.
Guarantees
In the callback-based API, when and how the callback gets called depends on the API implementor. For example, the callback may be called synchronously or asynchronously:
function doSomething(callback) {
if (Math.random() > 0.5) {
callback();
} else {
setTimeout(() => callback(), 1000);
}
}
The above design is strongly discouraged because it leads to the so-called "state of Zalgo". In the context of designing asynchronous APIs, this means a callback is called synchronously in some cases but asynchronously in other cases, creating ambiguity for the caller. For further background, see the article Designing APIs for Asynchrony, where the term was first formally presented. This API design makes side effects hard to analyze:
let value = 1;
doSomething(() => {
value = 2;
});
console.log(value); // 1 or 2?
On the other hand, promises are a form of inversion of control — the API implementor does not control when the callback gets called. Instead, the job of maintaining the callback queue and deciding when to call the callbacks is delegated to the promise implementation, and both the API user and API developer automatically get strong semantic guarantees, including:
- Callbacks added with
then()will never be invoked before the completion of the current run of the JavaScript event loop. - These callbacks will be invoked even if they were added after the success or failure of the asynchronous operation that the promise represents.
- Multiple callbacks may be added by calling
then()several times. They will be invoked one after another, in the order in which they were inserted.
To avoid surprises, functions passed to then() will never be called synchronously, even with an already-resolved promise:
Promise.resolve().then(() => console.log(2));
console.log(1);
// Logs: 1, 2
Instead of running immediately, the passed-in function is put on a microtask queue, which means it runs later (only after the function which created it exits, and when the JavaScript execution stack is empty), just before control is returned to the event loop; i.e., pretty soon:
const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
wait(0).then(() => console.log(4));
Promise.resolve()
.then(() => console.log(2))
.then(() => console.log(3));
console.log(1); // 1, 2, 3, 4
Task queues vs. microtasks
Promise callbacks are handled as a microtask whereas {{domxref("Window.setTimeout", "setTimeout()")}} callbacks are handled as task queues.
const promise = new Promise((resolve, reject) => {
console.log("Promise callback");
resolve();
}).then((result) => {
console.log("Promise callback (.then)");
});
setTimeout(() => {
console.log("event-loop cycle: Promise (fulfilled)", promise);
}, 0);
console.log("Promise (pending)", promise);
The code above will output:
Promise callback
Promise (pending) Promise {<pending>}
Promise callback (.then)
event-loop cycle: Promise (fulfilled) Promise {<fulfilled>}
For more details, refer to Tasks vs. microtasks.
When promises and tasks collide
If you run into situations in which you have promises and tasks (such as events or callbacks) which are firing in unpredictable orders, it's possible you may benefit from using a microtask to check status or balance out your promises when promises are created conditionally.
If you think microtasks may help solve this problem, see the microtask guide to learn more about how to use {{domxref("Window.queueMicrotask()", "queueMicrotask()")}} to enqueue a function as a microtask.
See also
- {{jsxref("Promise")}}
- {{jsxref("Statements/async_function", "async function")}}
- {{jsxref("Operators/await", "await")}}
- Promises/A+ specification
- We have a problem with promises on pouchdb.com (2015)
{{PreviousNext("Web/JavaScript/Guide/Using_classes", "Web/JavaScript/Guide/Typed_arrays")}}
{{PreviousNext("Web/JavaScript/Guide/Representing_dates_times", "Web/JavaScript/Guide/Indexed_collections")}}
Regular expressions are patterns used to match character combinations in strings. In JavaScript, regular expressions are also objects. These patterns are used with the {{jsxref("RegExp/exec", "exec()")}} and {{jsxref("RegExp/test", "test()")}} methods of {{jsxref("RegExp")}}, and with the {{jsxref("String/match", "match()")}}, {{jsxref("String/matchAll", "matchAll()")}}, {{jsxref("String/replace", "replace()")}}, {{jsxref("String/replaceAll", "replaceAll()")}}, {{jsxref("String/search", "search()")}}, and {{jsxref("String/split", "split()")}} methods of {{jsxref("String")}}. This chapter describes JavaScript regular expressions. It provides a brief overview of each syntax element. For a detailed explanation of each one's semantics, read the regular expressions reference.
Creating a regular expression
You construct a regular expression in one of two ways:
-
Using a regular expression literal, which consists of a pattern enclosed between slashes, as follows:
const re = /ab+c/;Regular expression literals provide compilation of the regular expression when the script is loaded. If the regular expression remains constant, using this can improve performance.
-
Or calling the constructor function of the {{jsxref("RegExp")}} object, as follows:
const re = new RegExp("ab+c");Using the constructor function provides runtime compilation of the regular expression. Use the constructor function when you know the regular expression pattern will be changing, or you don't know the pattern and are getting it from another source, such as user input.
Writing a regular expression pattern
A regular expression pattern is composed of simple characters, such as /abc/, or a combination of simple and special characters, such as /ab*c/ or /Chapter (\d+)\.\d*/.
The last example includes parentheses, which are used as a memory device.
The match made with this part of the pattern is remembered for later use, as described in Using groups.
Using simple patterns
Simple patterns are constructed of characters for which you want to find a direct match. For example, the pattern /abc/ matches character combinations in strings only when the exact sequence "abc" occurs (all characters together and in that order).
Such a match would succeed in the strings "Hi, do you know your abc's?" and "The latest airplane designs evolved from slabcraft.".
In both cases the match is with the substring "abc".
There is no match in the string "Grab crab" because while it contains the substring "ab c", it does not contain the exact substring "abc".
Using special characters
When the search for a match requires something more than a direct match, such as finding one or more b's, or finding white space, you can include special characters in the pattern.
For example, to match a single "a" followed by zero or more "b"s followed by "c", you'd use the pattern /ab*c/: the * after "b" means "0 or more occurrences of the preceding item."
In the string "cbbabbbbcdebc", this pattern will match the substring "abbbbc".
The following pages provide lists of the different special characters that fit into each category, along with descriptions and examples.
- Assertions guide
- : Assertions include boundaries, which indicate the beginnings and endings of lines and words, and other patterns indicating in some way that a match is possible (including look-ahead, look-behind, and conditional expressions).
- Character classes guide
- : Distinguish different types of characters. For example, distinguishing between letters and digits.
- Groups and backreferences guide
- : Groups group multiple patterns as a whole, and capturing groups provide extra submatch information when using a regular expression pattern to match against a string. Backreferences refer to a previously captured group in the same regular expression.
- Quantifiers guide
- : Indicate numbers of characters or expressions to match.
If you want to look at all the special characters that can be used in regular expressions in a single table, see the following:
| Characters / constructs | Corresponding article |
|---|---|
[xyz], [^xyz], .,
\d, \D, \w, \W,
\s, \S, \t, \r,
\n, \v, \f, [\b],
\0, \cX, \xHH,
\uHHHH, \u{H…H},
x|y
|
|
^, $, \b, \B,
x(?=y), x(?!y), (?<=y)x,
(?<!y)x
|
|
(x), (?<Name>x), (?:x),
\n, \k<Name>
|
|
x*, x+, x?,
x{n}, x{n,},
x{n,m}
|
Note
A larger cheat sheet is also available (only aggregating parts of those individual articles).
Escaping
If you need to use any of the special characters literally (actually searching for a "*", for instance), you should escape it by putting a backslash in front of it. For instance, to search for "a" followed by "*" followed by "b", you'd use /a\*b/ — the backslash "escapes" the "*", making it literal instead of special.
Note
In many cases, when trying match a special character, you can wrap it in a character class as an alternative to escaping, for example
/a[*]b/.
Similarly, if you're writing a regular expression literal and need to match a slash ("/"), you need to escape that (otherwise, it terminates the pattern).
For instance, to search for the string "/example/" followed by one or more alphabetic characters, you'd use /\/example\/[a-z]+/i—the backslashes before each slash make them literal.
To match a literal backslash, you need to escape the backslash.
For instance, to match the string "C:\" where "C" can be any letter, you'd use /[A-Z]:\\/ — the first backslash escapes the one after it, so the expression searches for a single literal backslash.
If using the RegExp constructor with a string literal, remember that the backslash is an escape in string literals, so to use it in the regular expression, you need to escape it at the string literal level.
/a\*b/ and new RegExp("a\\*b") create the same expression, which searches for "a" followed by a literal "*" followed by "b".
The {{jsxref("RegExp.escape()")}} function returns a new string where all special characters in regex syntax are escaped. This allows you to do new RegExp(RegExp.escape("a*b")) to create a regular expression that matches only the string "a*b".
Using parentheses
Parentheses around any part of the regular expression pattern causes that part of the matched substring to be remembered. Once remembered, the substring can be recalled for other use. See Groups and backreferences for more details.
Using regular expressions in JavaScript
Regular expressions are used with the {{jsxref("RegExp")}} methods {{jsxref("RegExp/test", "test()")}} and {{jsxref("RegExp/exec", "exec()")}} and with the {{jsxref("String")}} methods {{jsxref("String/match", "match()")}}, {{jsxref("String/matchAll", "matchAll()")}}, {{jsxref("String/replace", "replace()")}}, {{jsxref("String/replaceAll", "replaceAll()")}}, {{jsxref("String/search", "search()")}}, and {{jsxref("String/split", "split()")}}.
| Method | Description |
|---|---|
| {{jsxref("RegExp/exec", "exec()")}} | Executes a search for a match in a string. It returns an array of information or null on a mismatch. |
| {{jsxref("RegExp/test", "test()")}} | Tests for a match in a string. It returns true or false. |
| {{jsxref("String/match", "match()")}} | Returns an array containing all of the matches, including capturing groups, or null if no match is found. |
| {{jsxref("String/matchAll", "matchAll()")}} | Returns an iterator containing all of the matches, including capturing groups. |
| {{jsxref("String/search", "search()")}} | Tests for a match in a string. It returns the index of the match, or -1 if the search fails. |
| {{jsxref("String/replace", "replace()")}} | Executes a search for a match in a string, and replaces the matched substring with a replacement substring. |
| {{jsxref("String/replaceAll", "replaceAll()")}} | Executes a search for all matches in a string, and replaces the matched substrings with a replacement substring. |
| {{jsxref("String/split", "split()")}} | Uses a regular expression or a fixed string to break a string into an array of substrings. |
When you want to know whether a pattern is found in a string, use the test() or search() methods; for more information (but slower execution) use the exec() or match() methods.
If you use exec() or match() and if the match succeeds, these methods return an array and update properties of the associated regular expression object and also of the predefined regular expression object, RegExp.
If the match fails, the exec() method returns null (which coerces to false).
In the following example, the script uses the exec() method to find a match in a string.
const myRe = /d(b+)d/g;
const myArray = myRe.exec("cdbbdbsbz");
If you do not need to access the properties of the regular expression, an alternative way of creating myArray is with this script:
const myArray = /d(b+)d/g.exec("cdbbdbsbz");
// similar to 'cdbbdbsbz'.match(/d(b+)d/g); however,
// 'cdbbdbsbz'.match(/d(b+)d/g) outputs [ "dbbd" ]
// while /d(b+)d/g.exec('cdbbdbsbz') outputs [ 'dbbd', 'bb', index: 1, input: 'cdbbdbsbz' ]
(See Using the global search flag with exec() for further info about the different behaviors.)
If you want to construct the regular expression from a string, yet another alternative is this script:
const myRe = new RegExp("d(b+)d", "g");
const myArray = myRe.exec("cdbbdbsbz");
With these scripts, the match succeeds and returns the array and updates the properties shown in the following table.
| Object | Property or index | Description | In this example |
|---|---|---|---|
myArray |
The matched string and all remembered substrings. | ['dbbd', 'bb', index: 1, input: 'cdbbdbsbz'] |
|
index |
The 0-based index of the match in the input string. | 1 |
|
input |
The original string. | 'cdbbdbsbz' |
|
[0] |
The last matched characters. | 'dbbd' |
|
myRe |
lastIndex |
The index at which to start the next match. (This property is set only if the regular expression uses the g option, described in Advanced Searching With Flags.) | 5 |
source |
The text of the pattern. Updated at the time that the regular expression is created, not executed. | 'd(b+)d' |
As shown in the second form of this example, you can use a regular expression created with an object initializer without assigning it to a variable. If you do, however, every occurrence is a new regular expression. For this reason, if you use this form without assigning it to a variable, you cannot subsequently access the properties of that regular expression. For example, assume you have this script:
const myRe = /d(b+)d/g;
const myArray = myRe.exec("cdbbdbsbz");
console.log(`The value of lastIndex is ${myRe.lastIndex}`);
// "The value of lastIndex is 5"
However, if you have this script:
const myArray = /d(b+)d/g.exec("cdbbdbsbz");
console.log(`The value of lastIndex is ${/d(b+)d/g.lastIndex}`);
// "The value of lastIndex is 0"
The occurrences of /d(b+)d/g in the two statements are different regular expression objects and hence have different values for their lastIndex property.
If you need to access the properties of a regular expression created with an object initializer, you should first assign it to a variable.
Advanced searching with flags
Regular expressions have optional flags that allow for functionality like global searching and case-insensitive searching. These flags can be used separately or together in any order, and are included as part of the regular expression.
| Flag | Description | Corresponding property |
|---|---|---|
d |
Generate indices for substring matches. | {{jsxref("RegExp/hasIndices", "hasIndices")}} |
g |
Global search. | {{jsxref("RegExp/global", "global")}} |
i |
Case-insensitive search. | {{jsxref("RegExp/ignoreCase", "ignoreCase")}} |
m |
Makes ^ and $ match the start and end of each line instead of those of the entire string. |
{{jsxref("RegExp/multiline", "multiline")}} |
s |
Allows . to match newline characters. |
{{jsxref("RegExp/dotAll", "dotAll")}} |
u |
"Unicode"; treat a pattern as a sequence of Unicode code points. | {{jsxref("RegExp/unicode", "unicode")}} |
v |
An upgrade to the u mode with more Unicode features. |
{{jsxref("RegExp/unicodeSets", "unicodeSets")}} |
y |
Perform a "sticky" search that matches starting at the current position in the target string. | {{jsxref("RegExp/sticky", "sticky")}} |
To include a flag with the regular expression, use this syntax:
const re = /pattern/flags;
or
const re = new RegExp("pattern", "flags");
Note that the flags are an integral part of a regular expression. They cannot be added or removed later.
For example, re = /\w+\s/g creates a regular expression that looks for one or more characters followed by a space, and it looks for this combination throughout the string.
const re = /\w+\s/g;
const str = "fee fi fo fum";
const myArray = str.match(re);
console.log(myArray);
// ["fee ", "fi ", "fo "]
You could replace the line:
const re = /\w+\s/g;
with:
const re = new RegExp("\\w+\\s", "g");
and get the same result.
The m flag is used to specify that a multiline input string should be treated as multiple lines.
If the m flag is used, ^ and $ match at the start or end of any line within the input string instead of the start or end of the entire string.
The i, m, and s flags can be enabled or disabled for specific parts of a regex using the modifier syntax.
Using the global search flag with exec()
{{jsxref("RegExp.prototype.exec()")}} method with the g flag returns each match and its position iteratively.
const str = "fee fi fo fum";
const re = /\w+\s/g;
console.log(re.exec(str)); // ["fee ", index: 0, input: "fee fi fo fum"]
console.log(re.exec(str)); // ["fi ", index: 4, input: "fee fi fo fum"]
console.log(re.exec(str)); // ["fo ", index: 7, input: "fee fi fo fum"]
console.log(re.exec(str)); // null
In contrast, {{jsxref("String.prototype.match()")}} method returns all matches at once, but without their position.
console.log(str.match(re)); // ["fee ", "fi ", "fo "]
Using unicode regular expressions
The u flag is used to create "unicode" regular expressions; that is, regular expressions which support matching against unicode text. An important feature that's enabled in unicode mode is Unicode property escapes. For example, the following regular expression might be used to match against an arbitrary unicode "word":
/\p{L}*/u;
Unicode regular expressions have different execution behavior as well. RegExp.prototype.unicode contains more explanation about this.
Examples
Note
Several examples are also available in:
- The reference pages for {{jsxref("RegExp/exec", "exec()")}}, {{jsxref("RegExp/test", "test()")}}, {{jsxref("String/match", "match()")}}, {{jsxref("String/matchAll", "matchAll()")}}, {{jsxref("String/search", "search()")}}, {{jsxref("String/replace", "replace()")}}, {{jsxref("String/split", "split()")}}
- The guide articles: character classes, assertions, groups and backreferences, quantifiers
Using special characters to verify input
In the following example, the user is expected to enter a phone number. When the user presses the "Check" button, the script checks the validity of the number. If the number is valid (matches the character sequence specified by the regular expression), the script shows a message thanking the user and confirming the number. If the number is invalid, the script informs the user that the phone number is not valid.
The regular expression looks for:
- the beginning of the line of data:
^ - followed by three numeric characters
\d{3}OR|a left parenthesis\(, followed by three digits\d{3}, followed by a close parenthesis\), in a non-capturing group(?:) - followed by one dash, forward slash, or decimal point in a capturing group
() - followed by three digits
\d{3} - followed by the match remembered in the (first) captured group
\1 - followed by four digits
\d{4} - followed by the end of the line of data:
$
HTML
<p>
Enter your phone number (with area code) and then click "Check".
<br />
The expected format is like ###-###-####.
</p>
<form id="form">
<input id="phone" />
<button type="submit">Check</button>
</form>
<p id="output"></p>
JavaScript
const form = document.querySelector("#form");
const input = document.querySelector("#phone");
const output = document.querySelector("#output");
const re = /^(?:\d{3}|\(\d{3}\))([-/.])\d{3}\1\d{4}$/;
function testInfo(phoneInput) {
const ok = re.exec(phoneInput.value);
output.textContent = ok
? `Thanks, your phone number is ${ok[0]}`
: `${phoneInput.value} isn't a phone number with area code!`;
}
form.addEventListener("submit", (event) => {
event.preventDefault();
testInfo(input);
});
Result
{{EmbedLiveSample("Using_special_characters_to_verify_input")}}
Tools
- RegExr
- : An online tool to learn, build, & test Regular Expressions.
- Regex tester
- : An online regex builder/debugger
- Regex interactive tutorial
- : An online interactive tutorials, Cheat sheet, & Playground.
- Regex visualizer
- : An online visual regex tester.
{{PreviousNext("Web/JavaScript/Guide/Representing_dates_times", "Web/JavaScript/Guide/Indexed_collections")}}