RegExp.prototype.test() คืออะไร?
regex.test(string) ทดสอบว่ารูปแบบ (pattern) ตรงกับสตริงที่ให้มาหรือไม่ คืนค่า Boolean: true ถ้าตรง, false ถ้าไม่ตรง
- เร็วกว่า
match()หรือexec()เพราะไม่ต้องสร้าง array ผลลัพธ์ - เหมาะสำหรับ Validation, Condition checking
- ถ้ามี flag
gจะใช้lastIndex(stateful) - ต้องระวัง!
ตัวอย่างโค้ดครบถ้วน
<!DOCTYPE html>
<html lang="th">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>RegExp test() Demo</title>
<style>
body { font-family: 'Segoe UI', sans-serif; padding: 20px; max-width: 800px; margin: 0 auto; }
.panel { background: #f5f5f5; padding: 20px; border-radius: 8px; margin-bottom: 20px; }
.code { background: #2d2d2d; color: #f8f8f2; padding: 15px; border-radius: 4px; font-family: monospace; overflow-x: auto; }
.output { background: #e8f5e9; padding: 15px; border-radius: 4px; font-family: monospace; white-space: pre-wrap; }
.test-case { display: flex; gap: 10px; align-items: center; margin: 10px 0; flex-wrap: wrap; }
.test-case input { flex: 1; min-width: 200px; padding: 8px; font-family: monospace; border: 1px solid #ddd; border-radius: 4px; }
.test-case button { padding: 8px 16px; background: #7e32d4; color: white; border: none; border-radius: 4px; cursor: pointer; }
.result-badge { padding: 4px 12px; border-radius: 20px; font-weight: bold; font-size: 14px; }
.match { background: #c8e6c9; color: #2e7d32; }
.no-match { background: #ffcdd2; color: #c62828; }
table { width: 100%; border-collapse: collapse; margin-top: 10px; }
th, td { padding: 10px; border: 1px solid #ddd; text-align: left; }
th { background: #7e32d4; color: white; }
</style>
</head>
<body>
<h1>RegExp.prototype.test() - ทดสอบ Pattern Matching</h1>
<div class="panel">
<h3>ทดสอบแบบโต้ตอบ</h3>
<div class="test-case">
<label>Pattern: <input type="text" id="patternInput" value="\d{3}-\d{2}-\d{4}" /></label>
<label>Flags: <input type="text" id="flagsInput" value="" placeholder="g, i, m..." /></label>
</div>
<div class="test-case">
<label>Test String: <input type="text" id="testInput" value="My SSN is 123-45-6789" /></label>
<button onclick="runTest()">Test Match</button>
</div>
<div class="output" id="testOutput"></div>
</div>
<div class="panel">
<h3>ตัวอย่าง Patterns มาตรฐาน</h3>
<button onclick="demoCommonPatterns()">รันตัวอย่าง Patterns ทั่วไป</button>
<div class="output" id="commonOutput"></div>
</div>
<div class="panel">
<h3>⚠️ พัฒนาการ (Gotcha) กับ Flag 'g' (Global)</h3>
<button onclick="demoGlobalFlag()">สาธิตปัญหา Global Flag</button>
<div class="output" id="globalOutput"></div>
</div>
<script>
const testOutput = document.getElementById('testOutput');
const commonOutput = document.getElementById('commonOutput');
const globalOutput = document.getElementById('globalOutput');
function log(el, msg, isMatch = null) {
const div = document.createElement('div');
div.textContent = msg;
if (isMatch === true) div.className = 'result-badge match';
else if (isMatch === false) div.className = 'result-badge no-match';
el.appendChild(div);
}
function runTest() {
testOutput.innerHTML = '';
const pattern = document.getElementById('patternInput').value;
const flags = document.getElementById('flagsInput').value;
const testStr = document.getElementById('testInput').value;
try {
const regex = new RegExp(pattern, flags);
const result = regex.test(testStr);
log(testOutput, `Regex: ${regex.toString()}`, result);
log(testOutput, `Test string: "${testStr}"`);
log(testOutput, `Result: ${result}`, result);
} catch (e) {
log(testOutput, `Error: ${e.message}`);
}
}
function demoCommonPatterns() {
commonOutput.innerHTML = '';
const tests = [
{ pattern: /^\d{10}$/, test: '0812345678', desc: 'Thai Phone (10 digits)' },
{ pattern: /^[\w.-]+@[\w.-]+\.\w+$/, test: 'user@example.com', desc: 'Email (simple)' },
{ pattern: /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).{8,}$/, test: 'Pass1234', desc: 'Password: 8+ chars, upper, lower, digit' },
{ pattern: /^https?:\/\/.+/, test: 'https://example.com', desc: 'URL http/https' },
{ pattern: /^\d{4}-\d{2}-\d{2}$/, test: '2024-01-15', desc: 'Date YYYY-MM-DD' },
{ pattern: /^#[0-9A-Fa-f]{6}$/, test: '#FF5733', desc: 'Hex Color' },
{ pattern: /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/, test: '192.168.1.1', desc: 'IPv4 Address' },
];
tests.forEach(({ pattern, test, desc }) => {
const result = pattern.test(test);
log(commonOutput, `${desc}: ${pattern.toString()} → "${test}" = ${result}`, result);
});
}
function demoGlobalFlag() {
globalOutput.innerHTML = '';
const regex = /foo/g; // Global flag!
const str = 'foo bar foo baz';
log(globalOutput, `Regex: ${regex.toString()} (has 'g' flag)`);
log(globalOutput, `String: "${str}"`);
log(globalOutput, `---`);
// เรียก test() หลายครั้ง
log(globalOutput, `1st test: ${regex.test(str)} (lastIndex: ${regex.lastIndex})`);
log(globalOutput, `2nd test: ${regex.test(str)} (lastIndex: ${regex.lastIndex})`);
log(globalOutput, `3rd test: ${regex.test(str)} (lastIndex: ${regex.lastIndex})`);
log(globalOutput, `4th test: ${regex.test(str)} (lastIndex: ${regex.lastIndex})`);
log(globalOutput, `---`);
log(globalOutput, '⚠️ lastIndex ไม่ reset ระหว่างการเรียกต่าง string!');
// แก้ไข: reset lastIndex หรือสร้าง regex ใหม่ทุกครั้ง
log(globalOutput, '\n--- แก้ไข: ใช้ regex ใหม่ทุกครั้ง ---');
const str2 = 'foo bar';
log(globalOutput, `new RegExp('foo', 'g').test("${str2}") = ${new RegExp('foo', 'g').test(str2)}`);
log(globalOutput, `new RegExp('foo', 'g').test("${str2}") = ${new RegExp('foo', 'g').test(str2)}`);
}
</script>
</body>
</html>
พฤติกรรมสำคัญ: Flag 'g' (Global) และ lastIndex
const regex = /foo/g; const str = 'foo bar foo'; regex.test(str); // true - lastIndex = 3 regex.test(str); // true - lastIndex = 12 (เจอ "foo" ตัวที่ 2) regex.test(str); // false - lastIndex = 0 (reset หลังไม่เจอ) regex.test(str); // true - เริ่มใหม่จาก 0
⚠️ ปัญหา: ใช้ regex เดียวกับ 'g' flag ทดสอบ string คนละตัว
const regex = /cat/g;
regex.test('cat'); // true (lastIndex = 3)
regex.test('dog'); // false (lastIndex = 0 - reset เพราะไม่เจอ)
regex.test('cat'); // true (OK)
regex.test('cat'); // true (lastIndex = 3)
regex.test('cat'); // false! (lastIndex = 3, เริ่มค้นจาก index 3 ซึ่งไม่มี 'cat')
✅ แก้ไข: ไม่ใช้ 'g' flag สำหรับ test() หรือ Reset lastIndex
// วิธี 1: ไม่ใช้ g flag (แนะนำสำหรับ test)
const regex1 = /cat/; // ไม่มี g
regex1.test('cat'); // true
regex1.test('cat'); // true (เสมอ)
// วิธี 2: Reset lastIndex ก่อนใช้
const regex2 = /cat/g;
function testGlobal(regex, str) {
regex.lastIndex = 0;
return regex.test(str);
}
// วิธี 3: สร้าง regex ใหม่ทุกครั้ง (ถ้า pattern ไม่คงที่)
function createTester(pattern, flags = '') {
return (str) => new RegExp(pattern, flags.replace('g', '')).test(str);
}
การประยุกต์ใช้จริง: Form Validation
const validators = {
email: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
phone: /^0\d{9}$/,
password: /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&]).{8,}$/,
username: /^[a-zA-Z0-9_]{3,20}$/,
thaiId: /^\d{13}$/,
zipCode: /^\d{5}$/
};
function validateField(fieldName, value) {
const regex = validators[fieldName];
if (!regex) return { valid: true }; // ไม่มี rule
const isValid = regex.test(value);
return {
valid: isValid,
message: isValid ? '' : `รูปแบบ ${fieldName} ไม่ถูกต้อง`
};
}
// ใช้งาน
const emailResult = validateField('email', 'user@example.com');
// { valid: true, message: '' }
const phoneResult = validateField('phone', '0812345678');
// { valid: true, message: '' }
Performance: test() vs match() vs exec()
| Method | คืนค่า | ใช้เมื่อ | ความเร็ว |
|---|---|---|---|
regex.test(str) | boolean | แค่ต้องการรู้ว่าตรงหรือไม่ | ⚡ เร็วที่สุด |
str.match(regex) | Array / null | ต้องการข้อมูล match (capture groups) | ปานกลาง |
regex.exec(str) | Array / null | ต้องการข้อมูลละเอียด + ใช้กับ 'g' flag loop | ปานกลาง |
str.search(regex) | index / -1 | ต้องการตำแหน่งแรกที่เจอ | เร็ว |