Tất cả chúng ta, vào một thời điểm nào đó, đã từng nhìn vào một bức tường code JavaScript khủng khiếp và tự nguyền rủa trong lòng, biết rõ rằng phải có một cách tốt hơn. Sau một thời gian tìm hiểu, tôi đã tìm thấy một số dòng lệnh gọn gàng có thể xóa sổ nhiều dòng code dài dòng.
Đây là những mẹo thực sự hữu ích, dễ đọc, tận dụng các tính năng hiện đại của JavaScript để giải quyết các vấn đề phổ biến. Vì vậy, cho dù bạn đang dọn dẹp code hay chỉ mới bắt đầu một dự án mới, những thủ thuật này có thể giúp bạn có code thanh lịch và dễ bảo trì hơn.
Dưới đây là 9 dòng lệnh tiện lợi như vậy mà bạn có thể sử dụng ngay hôm nay. Hãy cùng khám phá xem chúng là gì nhé!
1. Làm phẳng mảng lồng nhau
Bạn đã bao giờ thử làm phẳng một mảng lồng nhau sâu chưa? Trước đây, điều đó đồng nghĩa với rất nhiều vòng lặp phức tạp, mảng tạm thời và quá nhiều code. Nhưng bây giờ nó được thực hiện rất gọn gàng trong một dòng lệnh mạnh mẽ:
const flattenArray = arr => arr.flat(Infinity); const nestedArray = [1, [2, 3, [4, 5, [6, 7]]], 8, [9, 10]];
const cleanArray = flattenArray(nestedArray);
// Result: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Nếu bạn làm điều này theo cách truyền thống hơn, bạn sẽ có một đoạn code như thế này:
function flattenTheHardWay(arr) { let result = []; for (let i = 0; i < arr.length; i++) { if (Array.isArray(arr[i])) { result = result.concat(flattenTheHardWay(arr[i])); } else { result.push(arr[i]); } } return result;
}
Tất cả công việc khó khăn được xử lý bởi flat(), và việc thêm Infinity cho nó biết phải đi xuống bất kỳ cấp độ nào. Đơn giản, sạch sẽ, và nó thực sự đã hoạt động.
2. Sao chép sâu đối tượng không cần thư viện
Nếu bạn cần một bản sao chép sâu thực sự của một đối tượng mà không cần sử dụng lodash? Dưới đây là một giải pháp không cần thư viện xử lý các đối tượng lồng nhau, mảng và thậm chí cả ngày tháng:
const deepClone = obj => JSON.parse(JSON.stringify(obj)); const complexObj = { user: { name: 'Alex', date: new Date() }, scores: [1, 2, [3, 4]], active: true
}; const cloned = deepClone(complexObj);
Đối với cách cũ? Bạn sẽ phải gõ một đoạn code như thế này:
function manualDeepClone(obj) { if (obj === null || typeof obj !== 'object') return obj; if (obj instanceof Date) return new Date(obj); const clone = Array.isArray(obj) ? [] : {}; for (let key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { clone[key] = manualDeepClone(obj[key]); } } return clone;
}
Lưu ý nhanh: Dòng lệnh này có một vài hạn chế - nó sẽ không xử lý các hàm, ký hiệu hoặc tham chiếu vòng. Nhưng đối với 90% trường hợp sử dụng, nó khá chính xác.
3. Chuyển đổi CSV sang mảng đối tượng
Đây là một dòng lệnh nhỏ gọn để lấy dữ liệu CSV và xuất ra một mảng các đối tượng có thể thao tác, lý tưởng để sử dụng trong phản hồi API hoặc đọc dữ liệu:
const csvToObjects = csv => csv.split('\n').map(row => Object.fromEntries(row.split(',').map((value, i, arr) => [arr[0].split(',')[i], value]))); const csvData = `name,age,city
Peboy,30,New York
Peace,25,San Francisco
Lara,35,Chicago`; const parsed = csvToObjects(csvData);
// Result:
// [
// { name: 'Peboy', age: '30', city: 'New York' },
// { name: 'Peace', age: '25', city: 'San Francisco' },
// { name: 'Lara', age: '35', city: 'Chicago' }
// ]
Nếu là cách làm cũ, bạn có thể sẽ viết một đoạn code như thế này:
function convertCSVTheHardWay(csv) { const lines = csv.split('\n'); const headers = lines[0].split(','); const result = []; for (let i = 1; i < lines.length; i++) { const obj = {}; const currentLine = lines[i].split(','); for (let j = 0; j < headers.length; j++) { obj[headers[j]] = currentLine[j]; } result.push(obj); } return result;
}
Đó là một cách hiệu quả để thực hiện chuyển đổi dữ liệu với một dòng lệnh, nhưng hãy thêm một số xử lý lỗi trước khi đưa nó vào sản xuất.
4. Xóa phần tử trùng lặp và sắp xếp mảng
Dưới đây là một dòng lệnh rút gọn để loại bỏ các phần tử trùng lặp và sắp xếp mảng của bạn cùng một lúc, hoàn hảo để làm sạch tập dữ liệu:
const uniqueSorted = arr => [...new Set(arr)].sort((a, b) => a - b); // Example of its use:
const messyArray = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5];
const cleaned = uniqueSorted(messyArray);
// Result: [1, 2, 3, 4, 5, 6, 9] // For string sorting const messyStrings = ['banana', 'apple', 'apple', 'cherry', 'banana'];
const cleanedStrings = [...new Set(messyStrings)].sort();
// Result: ['apple', 'banana', 'cherry']
Cách cũ trông như thế này:
function cleanArrayManually(arr) { const unique = []; for (let i = 0; i < arr.length; i++) { if (unique.indexOf(arr[i]) === -1) { unique.push(arr[i]); } } return unique.sort((a, b) => a - b);
}
Set sẽ lo việc loại bỏ trùng lặp một cách hoàn hảo, và sau đó toán tử spread sẽ biến nó trở lại thành một mảng. Và bạn chỉ cần gọi sort() sau đó!
5. Thao tác DOM: Truy vấn và biến đổi nhiều phần tử
Đây là một dòng lệnh mạnh mẽ cho phép bạn truy vấn và biến đổi nhiều phần tử DOM cùng một lúc:
const modifyElements = selector => Array.from(document.querySelectorAll(selector)).forEach(el => el.style); // Use it like this:
const updateButtons = modifyElements('.btn') .map(style => Object.assign(style, { backgroundColor: '#007bff', color: 'white', padding: '10px 20px'
})); // Or even simpler for class updates:
const toggleAll = selector => document.querySelectorAll(selector).forEach(el => el.classList.toggle('active'));
Cách tiếp cận truyền thống sẽ là:
function updateElementsManually(selector) { const elements = document.querySelectorAll(selector); for (let i = 0; i < elements.length; i++) { const el = elements[i]; el.style.backgroundColor = '#007bff'; el.style.color = 'white'; el.style.padding = '10px 20px'; }
}
Điều này hoạt động trong tất cả các trình duyệt hiện đại và giúp bạn tiết kiệm khỏi việc viết code thao tác DOM lặp đi lặp lại.
6. Gọi API song song với xử lý lỗi sạch
Đây là một dòng lệnh gọn gàng khác, thực hiện các lệnh gọi song song tới API và xử lý lỗi rất sạch sẽ.
const parallelCalls = urls => Promise.allSettled(urls.map(url => fetch(url).then(r => r.json()))); // Put it to work:
const urls = [ 'https://api.example.com/users', 'https://api.example.com/posts', 'https://api.example.com/comments'
]; // One line to fetch them all:
const getData = async () => { const results = await parallelCalls(urls); const [users, posts, comments] = results.map(r => r.status === 'fulfilled' ? r.value : null);
};
Dài dòng hơn sẽ là:
async function fetchDataManually(urls) { const results = []; for (const url of urls) { try { const response = await fetch(url); const data = await response.json(); results.push({ status: 'fulfilled', value: data }); } catch (error) { results.push({ status: 'rejected', reason: error }); } } return results;
}
Promise.allSettled là người hùng ở đây; nó không thất bại nếu một yêu cầu thất bại và nó trả về thông tin trạng thái rõ ràng cho mỗi lệnh gọi.
7. Định dạng ngày/giờ: Chuỗi ngày tháng gọn gàng không cần thư viện
Đây là một dòng lệnh tuyệt vời giúp biến đổi ngày tháng thành các chuỗi dễ đọc, gọn gàng mà không cần bất kỳ thư viện bên ngoài nào:
const formatDate = date => new Intl.DateTimeFormat('en-US', { dateStyle: 'full', timeStyle: 'short' }).format(date); const now = new Date();
console.log(formatDate(now));
// Output: "Thursday, December 9, 2024 at 2:30 PM" // Want a different format? Easy:
const shortDate = date => new Intl.DateTimeFormat('en-US', { month: 'short', day: 'numeric', year: 'numeric' }).format(date);
// Output: "Dec 9, 2024"
Cách cũ sẽ trông như thế này:
function formatDateManually(date) { const days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']; const months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; const dayName = days[date.getDay()]; const monthName = months[date.getMonth()]; const day = date.getDate(); const year = date.getFullYear(); const hours = date.getHours(); const minutes = date.getMinutes(); const ampm = hours >= 12 ? 'PM' : 'AM'; return `${dayName}, ${monthName} ${day}, ${year} at ${hours % 12}:${minutes.toString().padStart(2, '0')} ${ampm}`;
}
Intl.DateTimeFormat xử lý tất cả các công việc nặng nhọc, bao gồm cả bản địa hóa. Không còn phải xây dựng chuỗi ngày tháng thủ công nữa!
8. Xử lý sự kiện: Debounce không cần Bloat
Đây là một dòng lệnh gọn gàng để tạo phiên bản debounce của bất kỳ hàm nào - hoàn hảo cho việc xử lý input tìm kiếm hoặc thay đổi kích thước cửa sổ:
const debounce=(fn,ms)=>{let timeout;return(...args)=>{clearTimeout(timeout);timeout=setTimeout(()=>fn(...args),ms);};}; // Put it to work:
const expensiveSearch=query=>console.log('Searching for:',query);
const debouncedSearch=debounce(expensiveSearch,300); // Use it in your event listener:
searchInput.addEventListener('input',e=>debouncedSearch(e.target.value));
Cách truyền thống sẽ trông như thế này:
function createDebounce(fn, delay) { let timeoutId; return function debounced(...args) { if (timeoutId) { clearTimeout(timeoutId); } timeoutId = setTimeout(() => { fn.apply(this, args); timeoutId = null; }, delay); };
}
Dòng lệnh này bao gồm tất cả các trường hợp sử dụng debounce cơ bản và giúp bạn tiết kiệm khỏi việc gọi các hàm không cần thiết, đặc biệt là khi các input được tạo ra liên tục như gõ hoặc thay đổi kích thước.
9. Lưu trữ cục bộ: Lưu trữ đối tượng với xác thực
Đây chỉ là một dòng lệnh gọn gàng khác để xử lý việc lưu trữ đối tượng trong localStorage với xác thực tích hợp và xử lý lỗi:
const store = key => ({ get: () => JSON.parse(localStorage.getItem(key)), set: data => localStorage.setItem(key, JSON.stringify(data)), remove: () => localStorage.removeItem(key) }); // Use it like this:
const userStore = store('user'); // Save data
userStore.set({ id: 1, name: 'John', preferences: { theme: 'dark' } }); // Get data
const userData = userStore.get(); // Remove data
userStore.remove();
Cách cũ sẽ cần một cái gì đó như thế này:
function handleLocalStorage(key) { return { get: function() { try { const item = localStorage.getItem(key); return item ? JSON.parse(item) : null; } catch (e) { console.error('Error reading from localStorage', e); return null; } }, set: function(data) { try { localStorage.setItem(key, JSON.stringify(data)); } catch (e) { console.error('Error writing to localStorage', e); } }, remove: function() { try { localStorage.removeItem(key); } catch (e) { console.error('Error removing from localStorage', e); } } };
}
Bộ bao bọc này cung cấp cho bạn một API gọn gàng cho các thao tác localStorage và tự động xử lý tất cả việc phân tích cú pháp/chuỗi hóa JSON.
Kết luận
Những dòng lệnh này không chỉ là viết ít code hơn – mà còn là viết code thông minh hơn. Mỗi dòng giải quyết một thách thức JavaScript phổ biến một cách gọn gàng và dễ bảo trì. Mặc dù các đoạn code này rất mạnh mẽ, nhưng hãy nhớ rằng khả năng đọc luôn phải được ưu tiên. Nếu một dòng lệnh làm cho code của bạn khó hiểu hơn, hãy chia nó thành nhiều dòng.
Hãy thoải mái kết hợp và kết hợp các mẫu này trong dự án của bạn và đừng quên kiểm tra khả năng tương thích của trình duyệt đối với các tính năng JavaScript mới hơn như flat() hoặc Intl.DateTimeFormat nếu bạn đang hỗ trợ các trình duyệt cũ hơn.