JavaScript Regex Methods: test vs match vs exec
Deep dive into JavaScript regex methods: understand the differences between test, match, and exec, and learn when to use each.
JavaScript provides multiple regex methods, but beginners often confuse test, match, and exec. This article will compare these three methods in detail, helping you understand when to use which method to make your code more efficient.
Overview of Three Methods
Method Comparison Table
| Method | Return Value | Global Search | Use Case |
|---|---|---|---|
RegExp.test() | Boolean (true/false) | No/Yes (with g) | Quick validation of string matching |
String.match() | Array or null | No/Yes (with g) | Extract matching content |
RegExp.exec() | Match object or null | Manual control | Match one by one, get detailed info |
Decision Flowchart
Need to check if string matches?
→ Yes: use test()
Need to extract matching content?
→ Yes: Do you need position info?
→ No: use match()
→ Yes: use exec()
1. test() Method - Quick Validation
Basic Usage
test() is a method of RegExp object, returns a boolean indicating whether the string matches the regex.
const pattern = /\d+/;
const text = "Hello 123";
console.log(pattern.test(text)); // true
console.log(pattern.test("Hello")); // false
Use Cases
Case 1: Form Validation
function validateEmail(email) {
const pattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return pattern.test(email);
}
console.log(validateEmail("[email protected]")); // true
console.log(validateEmail("invalid-email")); // false
Case 2: Quick Check
const password = "MyPass123";
const hasUpperCase = /[A-Z]/.test(password);
const hasLowerCase = /[a-z]/.test(password);
const hasNumber = /\d/.test(password);
const hasMinLength = /.{8,}/.test(password);
if (hasUpperCase && hasLowerCase && hasNumber && hasMinLength) {
console.log("Password meets requirements");
}
Case 3: Conditional Logic
const url = "https://example.com/page";
if (/^https:\/\//.test(url)) {
console.log("This is a secure link");
} else if (/^http:\/\//.test(url)) {
console.log("This is an insecure link");
}
Global Flag Impact
const pattern = /\d/g;
const text = "a1b2c3";
// Each test() call continues from the last matched position
console.log(pattern.test(text)); // true (finds 1)
console.log(pattern.test(text)); // true (finds 2)
console.log(pattern.test(text)); // true (finds 3)
console.log(pattern.test(text)); // false (no more matches)
console.log(pattern.test(text)); // true (restarts)
// Reset lastIndex
pattern.lastIndex = 0;
console.log(pattern.test(text)); // true (restarts)
Recommendation: When using test() method, try to avoid the global flag g unless you explicitly need this behavior.
2. match() Method - Extract Content
Basic Usage
match() is a method of String object, returns an array of matches or null.
Non-global Match (without g)
const pattern = /(\d{2})-(\d{2})-(\d{4})/;
const text = "Date: 25-01-2024";
const result = text.match(pattern);
console.log(result);
/*
[
"25-01-2024", // Full match (index 0)
"25", // First capture group (index 1)
"01", // Second capture group (index 2)
"2024", // Third capture group (index 3)
index: 4, // Match start position
input: "Date: 25-01-2024", // Original string
groups: undefined // Named capture groups (if any)
]
*/
Global Match (with g)
const pattern = /\d+/g;
const text = "Count: 100, Price: 200, Total: 300";
const result = text.match(pattern);
console.log(result); // ["100", "200", "300"]
Use Cases
Case 1: Extract All Numbers
const text = "Order IDs: 1001, 1002, 1003";
const numbers = text.match(/\d+/g);
console.log(numbers); // ["1001", "1002", "1003"]
Case 2: Extract URL Parameters
const url = "https://example.com?name=John&age=30&city=NYC";
const params = {};
url.match(/[?&](\w+)=(\w+)/g)?.forEach(param => {
const [key, value] = param.slice(1).split('=');
params[key] = value;
});
console.log(params); // {name: "John", age: "30", city: "NYC"}
Case 3: Extract Emails
const text = "Contact: [email protected] or [email protected]";
const emails = text.match(/[\w.+-]+@[\w-]+\.[\w.-]+/g);
console.log(emails); // ["[email protected]", "[email protected]"]
Case 4: Parse Date Format
const pattern = /(\d{2})-(\d{2})-(\d{4})/;
const text = "Birthday: 25-01-2024";
const match = text.match(pattern);
if (match) {
const [full, day, month, year] = match;
console.log(`Day: ${day}, Month: ${month}, Year: ${year}`);
// Output: Day: 25, Month: 01, Year: 2024
}
Named Capture Groups
const pattern = /(?<day>\d{2})-(?<month>\d{2})-(?<year>\d{4})/;
const text = "Date: 25-01-2024";
const match = text.match(pattern);
if (match) {
console.log(match.groups.day); // 25
console.log(match.groups.month); // 01
console.log(match.groups.year); // 2024
}
3. exec() Method - Match One by One
Basic Usage
exec() is a method of RegExp object, each call returns a match object, and maintains match position internally.
Non-global Match
const pattern = /\d+/;
const text = "abc123def456";
console.log(pattern.exec(text));
/*
{
"0": "123",
"index": 3,
"input": "abc123def456",
"groups": undefined
}
*/
Global Match - Extract One by One
const pattern = /\d+/g;
const text = "abc123def456ghi789";
let match;
while ((match = pattern.exec(text)) !== null) {
console.log(`Found: ${match[0]}, Position: ${match.index}`);
}
// Output:
// Found: 123, Position: 3
// Found: 456, Position: 9
// Found: 789, Position: 15
Use Cases
Case 1: Need Position Information
const pattern = /error/gi;
const text = "Error at line 10, another error at line 20";
let match;
while ((match = pattern.exec(text)) !== null) {
console.log(`Found error: "${match[0]}" at position ${match.index}`);
}
Case 2: Complex Matching Logic
const text = "Username: john_doe, Email: [email protected], Phone: 123-456-7890";
const pattern = /(\w+):\s*([\w.-]+@[\w.-]+\.[a-z]+|\d{3}-\d{3}-\d{4})/g;
let match;
while ((match = pattern.exec(text)) !== null) {
const [full, field, value] = match;
console.log(`${field}: ${value}`);
}
Case 3: Build Parser
function parseMarkdownHeaders(text) {
const pattern = /^(#{1,6})\s+(.+)$/gm;
const headers = [];
let match;
while ((match = pattern.exec(text)) !== null) {
const level = match[1].length;
const content = match[2];
const position = match.index;
headers.push({ level, content, position });
}
return headers;
}
const markdown = `
# Level 1 Heading
## Level 2 Heading
### Level 3 Heading
`;
console.log(parseMarkdownHeaders(markdown));
Case 4: Need Match Info When Replacing
const text = "Price: 100, 200, 300";
const pattern = /(\d+)/g;
const result = text.replace(pattern, (match, p1, offset) => {
console.log(`Match: ${match}, Position: ${offset}`);
return parseInt(match) * 0.9; // Apply 10% discount
});
console.log(result); // Price: 90, 180, 270
Detailed Comparison
Performance Comparison
const text = "a1b2c3d4e5f6g7h8i9j0".repeat(1000);
const pattern = /\d/g;
// test() - Only checks if it matches
console.time('test');
for (let i = 0; i < 10000; i++) {
pattern.test(text);
}
console.timeEnd('test'); // Fastest
// match() - Returns all matches
console.time('match');
for (let i = 0; i < 10000; i++) {
text.match(pattern);
}
console.timeEnd('match'); // Faster
// exec() - Matches one by one
console.time('exec');
for (let i = 0; i < 10000; i++) {
pattern.lastIndex = 0;
let match;
while ((match = pattern.exec(text)) !== null) {}
}
console.timeEnd('exec'); // Slowest
Return Value Comparison
const pattern = /(\d{2})-(\d{2})/;
const text = "25-01";
// test()
const testResult = pattern.test(text);
console.log(testResult); // true (boolean)
// match()
const matchResult = text.match(pattern);
console.log(matchResult); // Array
/*
[
"25-01",
"25",
"01",
index: 0,
input: "25-01",
groups: undefined
]
*/
// exec()
const execResult = pattern.exec(text);
console.log(execResult); // Array (same as match())
/*
[
"25-01",
"25",
"01",
index: 0,
input: "25-01",
groups: undefined
]
*/
Real-world Case Comparison
Case 1: Validate Phone Number
Using test() - Recommended
function isValidPhone(phone) {
const pattern = /^\d{3}-\d{4}-\d{4}$/;
return pattern.test(phone);
}
console.log(isValidPhone("138-1234-5678")); // true
console.log(isValidPhone("123")); // false
Using match() - Unnecessary
function isValidPhone(phone) {
const pattern = /^\d{3}-\d{4}-\d{4}$/;
return phone.match(pattern) !== null;
}
// Same functionality, but returns complete match array (wastes resources)
Case 2: Extract All Emails
Using match() - Recommended
function extractEmails(text) {
const pattern = /[\w.+-]+@[\w-]+\.[\w.-]+/g;
return text.match(pattern) || [];
}
const text = "Contact: [email protected], [email protected]";
console.log(extractEmails(text)); // ["[email protected]", "[email protected]"]
Using exec() - Flexible but Complex
function extractEmails(text) {
const pattern = /[\w.+-]+@[\w-]+\.[\w.-]+/g;
const emails = [];
let match;
while ((match = pattern.exec(text)) !== null) {
emails.push(match[0]);
}
return emails;
}
// Same functionality, but more complex code
Case 3: Find and Replace with Position
Using exec() - Recommended
function replaceWithPosition(text, search) {
const pattern = new RegExp(search, 'g');
const result = [];
let match;
let lastIndex = 0;
while ((match = pattern.exec(text)) !== null) {
// Add text before match
result.push(text.slice(lastIndex, match.index));
// Add replacement text with position
result.push(`[${match[0]}@${match.index}]`);
lastIndex = pattern.lastIndex;
}
// Add remaining text
result.push(text.slice(lastIndex));
return result.join('');
}
const text = "apple banana apple";
console.log(replaceWithPosition(text, "apple"));
// Output: [apple@0] banana [apple@13]
Using match() - Can't Get Position
function replaceWithPosition(text, search) {
const pattern = new RegExp(search, 'g');
const matches = text.match(pattern) || [];
// Can't get position of each match!
// Can't complete the task this way
}
Best Practices
1. Prioritize Simplest Method
// Only need to validate → test()
if (/^\d+$/.test(text)) { /* ... */ }
// Need to extract all matches → match()
const emails = text.match(/[\w.+-]+@[\w-]+\.[\w.-]+/g);
// Need position info → exec()
while ((match = pattern.exec(text)) !== null) { /* ... */ }
2. Avoid Global Flag Side Effects
// Good practice: Create new object each time
function testGlobal(text, pattern) {
return new RegExp(pattern, 'g').test(text);
}
// Bad practice: Share global state
const pattern = /\d+/g;
function testGlobal2(text) {
return pattern.test(text); // lastIndex will accumulate
}
3. Use Named Capture Groups for Readability
// Good practice
const pattern = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
const match = text.match(pattern);
if (match) {
console.log(match.groups.year);
}
// Bad practice
const pattern = /(\d{4})-(\d{2})-(\d{2})/;
const match = text.match(pattern);
if (match) {
console.log(match[1]); // Need to remember index
}
4. Check for null
// match() and exec() may return null
const result = text.match(pattern);
if (result) {
// Process match result
} else {
// Handle no match case
}
Common Errors
Error 1: Forgetting to Check for null
const result = text.match(pattern);
console.log(result[0]); // Error if result is null!
// Correct practice
const result = text.match(pattern);
if (result) {
console.log(result[0]);
}
Error 2: Confusing test() and match()
// Error: Want to get match content but used test()
const result = pattern.test(text);
console.log(result[0]); // Error! result is boolean
// Correct: Use match()
const result = text.match(pattern);
if (result) {
console.log(result[0]);
}
Error 3: Global Flag Causes Problems
const pattern = /\d+/g;
const text = "123";
console.log(pattern.test(text)); // true
console.log(pattern.test(text)); // false (lastIndex moved)
console.log(pattern.test(text)); // true (reset)
Performance Tips
- Only need validation: Use
test()- fastest - Extract all matches: Use
match()- fast and concise - Need position info: Use
exec()- most complete functionality - Avoid unnecessary global searches: Only use
gflag when needed - Pre-compile regex: If using multiple times, create RegExp object first
Summary
| Scenario | Recommended Method | Reason |
|---|---|---|
| Form validation | test() | Fast boolean return |
| Quick check | test() | Simplest and most direct |
| Extract all matches | match() | Returns all results at once |
| Simple extraction | match() | Concise and readable code |
| Need position info | exec() | Provides index information |
| Process one by one | exec() | Can process matches individually |
| Complex parsing | exec() | Most flexible control |
Remember: Choosing the right method makes your code clearer and more efficient!
Use our online Regex Tester to test these methods and see different results immediately!
About the Author
The Regex Master Team consists of experienced developers and technical writers dedicated to simplifying regular expressions for everyone. We ensure all patterns are rigorously tested and verified to provide accurate, production-ready solutions.
Try It: Regex Tester
Use our interactive regex tester to experiment with the patterns you learned in this article. Test your regular expressions in real-time and see immediate results.