สำรวจพรมแดนใหม่ของ JavaScript กับคู่มือฉบับสมบูรณ์เกี่ยวกับ Property Pattern Matching เรียนรู้ syntax, เทคนิคขั้นสูง และกรณีการใช้งานจริง
ปลดล็อกอนาคตของ JavaScript: เจาะลึก Property Pattern Matching
ในโลกของการพัฒนาซอฟต์แวร์ที่มีการเปลี่ยนแปลงอยู่ตลอดเวลา นักพัฒนาต่างแสวงหาเครื่องมือและกระบวนทัศน์ที่ทำให้โค้ดอ่านง่ายขึ้น บำรุงรักษาได้ดีขึ้น และแข็งแกร่งขึ้น เป็นเวลาหลายปีที่นักพัฒนา JavaScript มองภาษาอย่าง Rust, Elixir และ F# ด้วยความอิจฉาในฟีเจอร์ที่ทรงพลังอย่างหนึ่ง นั่นคือ pattern matching ข่าวดีก็คือ ฟีเจอร์ที่ปฏิวัติวงการนี้กำลังจะมาถึง JavaScript ในไม่ช้า และการประยุกต์ใช้ที่ส่งผลกระทบมากที่สุดอาจเป็นวิธีที่เราทำงานกับอ็อบเจกต์
คู่มือนี้จะพาคุณเจาะลึกฟีเจอร์ Property Pattern Matching ที่ถูกเสนอสำหรับ JavaScript เราจะสำรวจว่ามันคืออะไร ปัญหาที่มันช่วยแก้ไข syntax ที่ทรงพลังของมัน และสถานการณ์จริงในทางปฏิบัติที่จะเปลี่ยนวิธีที่คุณเขียนโค้ด ไม่ว่าคุณจะกำลังประมวลผลการตอบกลับ API ที่ซับซ้อน จัดการสถานะของแอปพลิเคชัน หรือจัดการกับโครงสร้างข้อมูลหลายรูปแบบ (polymorphic data structures) pattern matching จะกลายเป็นเครื่องมือที่ขาดไม่ได้ในคลังแสง JavaScript ของคุณ
Pattern Matching คืออะไรกันแน่?
โดยแก่นแท้แล้ว pattern matching คือกลไกสำหรับการตรวจสอบค่ากับชุดของ "แพตเทิร์น" แพตเทิร์นจะอธิบายรูปทรงและคุณสมบัติของข้อมูลที่คุณคาดหวัง หากค่าตรงกับแพตเทิร์น บล็อกโค้ดที่สอดคล้องกันจะถูกดำเนินการ ลองนึกภาพว่ามันคือคำสั่ง `switch` ที่ทรงพลังยิ่งกว่า ซึ่งไม่เพียงแต่สามารถตรวจสอบค่าธรรมดาอย่างสตริงหรือตัวเลขได้ แต่ยังสามารถตรวจสอบโครงสร้างของข้อมูลของคุณได้ รวมถึงคุณสมบัติของอ็อบเจกต์ของคุณด้วย
แต่มันเป็นมากกว่าแค่คำสั่ง `switch` เพราะ pattern matching ผสมผสานแนวคิดที่ทรงพลังสามอย่างเข้าด้วยกัน:
- Inspection (การตรวจสอบ): มันจะตรวจสอบว่าอ็อบเจกต์มีโครงสร้างที่แน่นอนหรือไม่ (เช่น มีคุณสมบัติ `status` เท่ากับ 'success' หรือไม่)
- Destructuring (การสลายโครงสร้าง): หากโครงสร้างตรงกัน มันสามารถดึงค่าจากภายในโครงสร้างนั้นออกมาใส่ในตัวแปรท้องถิ่นได้พร้อมกัน
- Control Flow (การควบคุมการทำงาน): มันจะควบคุมทิศทางการทำงานของโปรแกรมตามแพตเทิร์นที่จับคู่ได้สำเร็จ
การผสมผสานนี้ช่วยให้คุณสามารถเขียนโค้ดเชิงประกาศ (declarative) ที่แสดงเจตนาของคุณได้อย่างชัดเจน แทนที่จะเขียนลำดับคำสั่งเชิงบังคับ (imperative) เพื่อตรวจสอบและแยกข้อมูล คุณเพียงแค่อธิบายรูปทรงของข้อมูลที่คุณสนใจ และ pattern matching จะจัดการส่วนที่เหลือให้เอง
ปัญหา: โลกของการตรวจสอบอ็อบเจกต์ที่ยืดยาว
ก่อนที่เราจะไปดูวิธีแก้ปัญหา เรามาทำความเข้าใจกับปัญหากันก่อน นักพัฒนา JavaScript ทุกคนเคยเขียนโค้ดที่หน้าตาประมาณนี้ ลองจินตนาการว่าเรากำลังจัดการกับการตอบกลับจาก API ซึ่งสามารถแสดงสถานะต่างๆ ของคำขอข้อมูลผู้ใช้ได้
function handleApiResponse(response) {
if (response && typeof response === 'object') {
if (response.status === 'success' && response.data) {
if (Array.isArray(response.data.users) && response.data.users.length > 0) {
console.log(`Processing ${response.data.users.length} users.`);
// ... logic to process users
} else {
console.log('Request successful, but no users found.');
}
} else if (response.status === 'error') {
if (response.error && response.error.code === 404) {
console.error('Error: The requested resource was not found.');
} else if (response.error && response.error.code >= 500) {
console.error(`A server error occurred: ${response.error.message}`);
} else {
console.error('An unknown error occurred.');
}
} else if (response.status === 'pending') {
console.log('The request is still pending. Please wait.');
} else {
console.warn('Received an unrecognized response structure.');
}
} else {
console.error('Invalid response format received.');
}
}
โค้ดนี้ทำงานได้ แต่มีปัญหาหลายอย่าง:
- ความซับซ้อนเชิงไซโคลเมติกสูง (High Cyclomatic Complexity): คำสั่ง `if/else` ที่ซ้อนกันลึกๆ สร้างเว็บของตรรกะที่ซับซ้อนซึ่งยากต่อการติดตามและทดสอบ
- เกิดข้อผิดพลาดได้ง่าย (Error-Prone): เป็นเรื่องง่ายที่จะลืมตรวจสอบ `null` หรือสร้างบั๊กทางตรรกะ ตัวอย่างเช่น จะเกิดอะไรขึ้นถ้า `response.data` มีอยู่ แต่ `response.data.users` ไม่มี? สิ่งนี้อาจนำไปสู่ข้อผิดพลาดขณะรันไทม์
- อ่านยาก (Poor Readability): เจตนาของโค้ดถูกบดบังด้วยโค้ดสำเร็จรูป (boilerplate) ของการตรวจสอบการมีอยู่, ประเภท และค่าต่างๆ เป็นการยากที่จะเห็นภาพรวมอย่างรวดเร็วของรูปทรงการตอบกลับที่เป็นไปได้ทั้งหมดที่ฟังก์ชันนี้จัดการ
- บำรุงรักษายาก (Difficult to Maintain): การเพิ่มสถานะการตอบกลับใหม่ (เช่น สถานะ `'throttled'`) จำเป็นต้องหาตำแหน่งที่เหมาะสมในการแทรกบล็อก `else if` อีกบล็อกอย่างระมัดระวัง ซึ่งเพิ่มความเสี่ยงที่จะเกิดการถดถอย (regression)
ทางออก: การจับคู่เชิงประกาศด้วย Property Patterns
ทีนี้ เรามาดูกันว่า Property Pattern Matching สามารถปรับปรุงตรรกะที่ซับซ้อนนี้ให้กลายเป็นสิ่งที่สะอาดตา เป็นเชิงประกาศ และแข็งแกร่งได้อย่างไร syntax ที่เสนอนี้ใช้นิพจน์ `match` ซึ่งจะประเมินค่าเทียบกับชุดของ `case` clause
ข้อจำกัดความรับผิดชอบ: syntax สุดท้ายอาจมีการเปลี่ยนแปลงในขณะที่ข้อเสนอนี้ผ่านกระบวนการของ TC39 ตัวอย่างด้านล่างนี้อ้างอิงจากสถานะปัจจุบันของข้อเสนอ
function handleApiResponseWithPatternMatching(response) {
match (response) {
case { status: 'success', data: { users: [firstUser, ...rest] } }:
console.log(`Processing ${1 + rest.length} users.`);
// ... logic to process users
break;
case { status: 'success' }:
console.log('Request successful, but no users found or data is in an unexpected format.');
break;
case { status: 'error', error: { code: 404 } }:
console.error('Error: The requested resource was not found.');
break;
case { status: 'error', error: { code: as c, message: as msg } } if (c >= 500):
console.error(`A server error occurred (${c}): ${msg}`);
break;
case { status: 'error' }:
console.error('An unknown error occurred.');
break;
case { status: 'pending' }:
console.log('The request is still pending. Please wait.');
break;
default:
console.error('Invalid or unrecognized response format received.');
break;
}
}
ความแตกต่างนั้นชัดเจนราวกับกลางวันและกลางคืน โค้ดนี้คือ:
- แบนและอ่านง่าย (Flat and Readable): โครงสร้างแบบเส้นตรงทำให้ง่ายต่อการเห็นกรณีที่เป็นไปได้ทั้งหมดได้อย่างรวดเร็ว แต่ละ `case` จะอธิบายรูปทรงของข้อมูลที่มันจัดการอย่างชัดเจน
- เป็นเชิงประกาศ (Declarative): เราอธิบายว่า อะไร ที่เรากำลังมองหา ไม่ใช่ วิธี การตรวจสอบมัน
- ปลอดภัย (Safe): แพตเทิร์นจะจัดการการตรวจสอบคุณสมบัติที่เป็น `null` หรือ `undefined` ตลอดเส้นทางโดยปริยาย หาก `response.error` ไม่มีอยู่ แพตเทิร์นที่เกี่ยวข้องกับมันก็จะไม่ตรงกัน ป้องกันข้อผิดพลาดขณะรันไทม์
- บำรุงรักษาง่าย (Maintainable): การเพิ่ม case ใหม่ทำได้ง่ายเพียงแค่เพิ่มบล็อก `case` อีกบล็อก โดยมีความเสี่ยงต่อตรรกะที่มีอยู่น้อยที่สุด
เจาะลึก: เทคนิค Property Pattern Matching ขั้นสูง
Property pattern matching นั้นมีความหลากหลายอย่างไม่น่าเชื่อ เรามาดูเทคนิคสำคัญที่ทำให้มันทรงพลังกัน
1. การจับคู่ค่าคุณสมบัติและการผูกตัวแปร
แพตเทิร์นพื้นฐานที่สุดคือการตรวจสอบการมีอยู่และค่าของคุณสมบัติ แต่พลังที่แท้จริงของมันมาจากการผูกค่าคุณสมบัติอื่น ๆ เข้ากับตัวแปรใหม่
const user = {
id: 'user-123',
role: 'admin',
preferences: {
theme: 'dark',
language: 'en'
}
};
match (user) {
// Match the role and bind the id to a new variable 'userId'
case { role: 'admin', id: as userId }:
console.log(`Admin user detected with ID: ${userId}`);
// 'userId' is now 'user-123'
break;
// Using shorthand similar to object destructuring
case { role: 'editor', id }:
console.log(`Editor user detected with ID: ${id}`);
break;
default:
console.log('User is not a privileged user.');
break;
}
ในตัวอย่าง `id: as userId` และชวเลข `id` ทั้งคู่ตรวจสอบการมีอยู่ของคุณสมบัติ `id` และผูกค่าของมันเข้ากับตัวแปร (`userId` หรือ `id`) ที่สามารถใช้ได้ภายในขอบเขตของบล็อก `case` สิ่งนี้รวมการตรวจสอบและการดึงค่าเข้าด้วยกันเป็นการดำเนินการเดียวที่สง่างาม
2. แพตเทิร์นอ็อบเจกต์และอาร์เรย์แบบซ้อนกัน
แพตเทิร์นสามารถซ้อนกันได้ทุกระดับความลึก ช่วยให้คุณสามารถตรวจสอบและสลายโครงสร้างข้อมูลที่ซับซ้อนและเป็นลำดับชั้นได้อย่างง่ายดายในเชิงประกาศ
function getPrimaryContact(data) {
match (data) {
// Match a deeply nested email property
case { user: { contacts: { email: as primaryEmail } } }:
console.log(`Primary email found: ${primaryEmail}`);
break;
// Match if the 'contacts' is an array with at least one item
case { user: { contacts: [firstContact, ...rest] } } if (firstContact.type === 'email'):
console.log(`First contact email is: ${firstContact.value}`);
break;
default:
console.log('No primary contact information available in the expected format.');
break;
}
}
getPrimaryContact({ user: { contacts: { email: 'test@example.com' } } });
getPrimaryContact({ user: { contacts: [{ type: 'email', value: 'info@example.com' }, { type: 'phone', value: '123' }] } });
สังเกตว่าเราสามารถผสมผสานแพตเทิร์นคุณสมบัติของอ็อบเจกต์ (`{ user: ... }`) กับแพตเทิร์นของอาร์เรย์ (`[firstContact, ...rest]`) ได้อย่างราบรื่น เพื่ออธิบายรูปทรงของข้อมูลที่เราต้องการได้อย่างแม่นยำ
3. การใช้ Guards (`if` clauses) สำหรับตรรกะที่ซับซ้อน
บางครั้ง การจับคู่รูปทรงอย่างเดียวไม่เพียงพอ คุณอาจต้องตรวจสอบเงื่อนไขที่ขึ้นอยู่กับ ค่า ของคุณสมบัติ นี่คือจุดที่ guards เข้ามามีบทบาท สามารถเพิ่ม `if` clause เข้าไปใน `case` เพื่อให้มีการตรวจสอบบูลีนเพิ่มเติมตามต้องการได้
`case` จะจับคู่ได้ก็ต่อเมื่อทั้งแพตเทิร์นมีโครงสร้างที่ถูกต้อง และเงื่อนไขของ guard ประเมินค่าเป็น `true`
function processTransaction(tx) {
match (tx) {
case { type: 'purchase', amount } if (amount > 1000):
console.log(`High-value purchase of ${amount} requires fraud check.`);
break;
case { type: 'purchase' }:
console.log('Standard purchase processed.');
break;
case { type: 'refund', originalTx: { date: as txDate } } if (isOlderThan30Days(txDate)):
console.log('Refund request is outside the allowable 30-day window.');
break;
case { type: 'refund' }:
console.log('Refund processed.');
break;
default:
console.log('Unknown transaction type.');
break;
}
}
Guards เป็นสิ่งจำเป็นสำหรับการเพิ่มตรรกะที่กำหนดเองซึ่งนอกเหนือไปจากการตรวจสอบโครงสร้างหรือค่าที่เท่ากันแบบง่ายๆ ทำให้ pattern matching เป็นเครื่องมือที่ครอบคลุมอย่างแท้จริงสำหรับการจัดการกฎทางธุรกิจที่ซับซ้อน
4. Rest Property (`...`) สำหรับการรวบรวมคุณสมบัติที่เหลือ
เช่นเดียวกับใน object destructuring คุณสามารถใช้ rest syntax (`...`) เพื่อรวบรวมคุณสมบัติทั้งหมดที่ไม่ได้ระบุไว้อย่างชัดเจนในแพตเทิร์น นี่มีประโยชน์อย่างยิ่งสำหรับการส่งต่อข้อมูลหรือสร้างอ็อบเจกต์ใหม่โดยไม่มีคุณสมบัติบางอย่าง
function logUserAndForwardData(event) {
match (event) {
case { type: 'user_login', timestamp, userId, ...restOfData }:
console.log(`User ${userId} logged in at ${new Date(timestamp).toISOString()}`);
// Forward the rest of the data to another service
analyticsService.track('login', restOfData);
break;
case { type: 'user_logout', userId, ...rest }:
console.log(`User ${userId} logged out.`);
// The 'rest' object will contain any other properties on the event
break;
default:
// Handle other event types
break;
}
}
กรณีการใช้งานจริงและตัวอย่างจากโลกแห่งความเป็นจริง
มาเปลี่ยนจากทฤษฎีสู่การปฏิบัติกันดีกว่า property pattern matching จะส่งผลกระทบที่ยิ่งใหญ่ที่สุดต่องานประจำวันของคุณที่ไหนบ้าง?
กรณีการใช้งานที่ 1: การจัดการสถานะใน UI Frameworks (React, Vue, ฯลฯ)
การพัฒนา front-end สมัยใหม่นั้นเกี่ยวกับการจัดการสถานะเป็นหลัก คอมโพเนนต์มักจะอยู่ในสถานะใดสถานะหนึ่งจากหลายสถานะที่แยกจากกัน: `idle`, `loading`, `success`, หรือ `error` Pattern matching เหมาะอย่างยิ่งสำหรับการเรนเดอร์ UI ตามอ็อบเจกต์สถานะนี้
พิจารณาคอมโพเนนต์ React ที่กำลังดึงข้อมูล:
// State object could look like:
// { status: 'loading' }
// { status: 'success', data: [...] }
// { status: 'error', error: { message: '...' } }
function DataDisplay({ state }) {
// The match expression can return a value (like JSX)
return match (state) {
case { status: 'loading' }:
return <Spinner />;
case { status: 'success', data }:
return <DataTable items={data} />;
case { status: 'error', error: { message } }:
return <ErrorDisplay message={message} />;
default:
return <p>Please click the button to fetch data.</p>;
};
}
วิธีนี้เป็นเชิงประกาศมากกว่าและมีโอกาสเกิดข้อผิดพลาดน้อยกว่าการใช้ `if (state.status === ...)` ต่อกันเป็นทอดๆ มันจัดวางรูปทรงของสถานะไว้คู่กับ UI ที่สอดคล้องกัน ทำให้ตรรกะของคอมโพเนนต์เข้าใจได้ในทันที
กรณีการใช้งานที่ 2: การจัดการเหตุการณ์และการกำหนดเส้นทางขั้นสูง
ในสถาปัตยกรรมที่ขับเคลื่อนด้วยข้อความ (message-driven) หรือตัวจัดการเหตุการณ์ที่ซับซ้อน คุณมักจะได้รับอ็อบเจกต์เหตุการณ์ที่มีรูปทรงแตกต่างกันไป Pattern matching เป็นวิธีที่สง่างามในการกำหนดเส้นทางเหตุการณ์เหล่านี้ไปยังตรรกะที่ถูกต้อง
function handleSystemEvent(event) {
match (event) {
case { type: 'payment', payload: { method: 'credit_card', amount } }:
processCreditCardPayment(amount, event.payload);
break;
case { type: 'payment', payload: { method: 'paypal', transactionId } }:
verifyPaypalPayment(transactionId);
break;
case { type: 'notification', payload: { recipient, message } } if (recipient.startsWith('sms:')):
sendSmsNotification(recipient, message);
break;
case { type: 'notification', payload: { recipient, message } } if (recipient.includes('@')):
sendEmailNotification(recipient, message);
break;
default:
logUnhandledEvent(event.type);
break;
}
}
กรณีการใช้งานที่ 3: การตรวจสอบและประมวลผลอ็อบเจกต์การกำหนดค่า
เมื่อแอปพลิเคชันของคุณเริ่มต้นขึ้น บ่อยครั้งที่ต้องประมวลผลอ็อบเจกต์การกำหนดค่า Pattern matching สามารถช่วยตรวจสอบการกำหนดค่านี้และตั้งค่าแอปพลิเคชันได้อย่างเหมาะสม
function initializeApp(config) {
console.log('Initializing application...');
match (config) {
case { mode: 'production', api: { url: apiUrl }, logging: { level: 'error' } }:
configureForProduction(apiUrl, 'error');
break;
case { mode: 'development', api: { url: apiUrl, mock: true } }:
configureForDevelopment(apiUrl, true);
break;
case { mode: 'development', api: { url } }:
configureForDevelopment(url, false);
break;
default:
throw new Error('Invalid or incomplete configuration provided.');
}
}
ประโยชน์ของการนำ Property Pattern Matching มาใช้
- ความชัดเจนและความสามารถในการอ่าน (Clarity and Readability): โค้ดจะกลายเป็นเอกสารในตัวเอง บล็อก `match` ทำหน้าที่เป็นรายการที่ชัดเจนของโครงสร้างข้อมูลที่โค้ดของคุณคาดว่าจะจัดการ
- ลดโค้ดสำเร็จรูป (Reduced Boilerplate): บอกลาห่วงโซ่ `if-else` ที่ซ้ำซากและยืดยาว, การตรวจสอบ `typeof`, และการป้องกันการเข้าถึงคุณสมบัติ
- เพิ่มความปลอดภัย (Enhanced Safety): ด้วยการจับคู่ตามโครงสร้าง คุณจะหลีกเลี่ยงข้อผิดพลาด `TypeError: Cannot read properties of undefined` หลายอย่างที่รบกวนแอปพลิเคชัน JavaScript ได้โดยธรรมชาติ
- ปรับปรุงการบำรุงรักษา (Improved Maintainability): ลักษณะที่แบนและแยกออกจากกันของบล็อก `case` ทำให้ง่ายต่อการเพิ่ม, ลบ, หรือแก้ไขตรรกะสำหรับรูปทรงข้อมูลที่เฉพาะเจาะจงโดยไม่ส่งผลกระทบต่อกรณีอื่น ๆ
- การเตรียมพร้อมสำหรับอนาคตด้วยการตรวจสอบความครบถ้วน (Exhaustiveness Checking): เป้าหมายสำคัญของข้อเสนอ TC39 คือการเปิดใช้งานการตรวจสอบความครบถ้วนในที่สุด ซึ่งหมายความว่าคอมไพเลอร์หรือรันไทม์สามารถเตือนคุณได้หากบล็อก `match` ของคุณไม่ได้จัดการกับทุกรูปแบบที่เป็นไปได้ของประเภท ซึ่งจะช่วยกำจัดบั๊กทั้งประเภทได้อย่างมีประสิทธิภาพ
สถานะปัจจุบันและวิธีทดลองใช้ในวันนี้
ณ ปลายปี 2023 ข้อเสนอ Pattern Matching อยู่ใน Stage 1 ของกระบวนการ TC39 ซึ่งหมายความว่าฟีเจอร์นี้กำลังอยู่ในระหว่างการสำรวจและกำหนดอย่างจริงจัง แต่ยังไม่ได้เป็นส่วนหนึ่งของมาตรฐาน ECMAScript อย่างเป็นทางการ syntax และความหมายอาจยังมีการเปลี่ยนแปลงก่อนที่จะสรุปผล
ดังนั้น คุณ ไม่ควร ใช้มันในโค้ดที่ใช้งานจริง (production) ที่กำหนดเป้าหมายไปยังเบราว์เซอร์มาตรฐานหรือสภาพแวดล้อม Node.js ในตอนนี้
อย่างไรก็ตาม คุณสามารถทดลองใช้ได้แล้ววันนี้โดยใช้ Babel! คอมไพเลอร์ JavaScript ช่วยให้คุณสามารถใช้ฟีเจอร์ในอนาคตและแปลง (transpile) ให้เป็นโค้ดที่เข้ากันได้ หากต้องการลองใช้ pattern matching คุณสามารถใช้ปลั๊กอิน `@babel/plugin-proposal-pattern-matching`
ข้อควรระวัง
แม้ว่าจะสนับสนุนให้ทดลองใช้ แต่โปรดจำไว้ว่าคุณกำลังทำงานกับฟีเจอร์ที่ถูกเสนอ การพึ่งพามันสำหรับโครงการที่สำคัญมีความเสี่ยงจนกว่าจะถึง Stage 3 หรือ 4 ของกระบวนการ TC39 และได้รับการสนับสนุนอย่างกว้างขวางในเอนจิ้น JavaScript หลัก ๆ
สรุป: อนาคตคือเชิงประกาศ
Property Pattern Matching แสดงถึงการเปลี่ยนแปลงกระบวนทัศน์ที่สำคัญสำหรับ JavaScript มันผลักดันเราจากการตรวจสอบข้อมูลแบบทีละขั้นตอนเชิงบังคับไปสู่รูปแบบการเขียนโปรแกรมที่เป็นเชิงประกาศ, สื่อความหมายได้ดี และแข็งแกร่งมากขึ้น
ด้วยการช่วยให้เราอธิบาย "อะไร" (รูปทรงของข้อมูลของเรา) แทนที่จะเป็น "อย่างไร" (ขั้นตอนที่น่าเบื่อของการตรวจสอบและดึงค่า) มันสัญญาว่าจะช่วยทำความสะอาดส่วนที่ซับซ้อนและเกิดข้อผิดพลาดได้ง่ายที่สุดใน codebase ของเรา ตั้งแต่การจัดการข้อมูล API ไปจนถึงการจัดการสถานะและการกำหนดเส้นทางเหตุการณ์ การใช้งานของมันกว้างขวางและส่งผลกระทบอย่างมาก
จับตาดูความคืบหน้าของข้อเสนอ TC39 อย่างใกล้ชิด เริ่มทดลองใช้ในโครงการส่วนตัวของคุณ อนาคตเชิงประกาศของ JavaScript กำลังก่อตัวขึ้น และ pattern matching คือหัวใจสำคัญของมัน