JS重复代码重构为函数求助,联系人管理功能代码优化
优化通讯录编辑/删除功能的DRY原则实践
首先,咱们先梳理下你代码里的核心问题:
- 重复的循环逻辑(多次遍历
contactsBook查找联系人、多次遍历UL子节点找LI)违反了DRY(Don't Repeat Yourself)原则,不仅冗余还容易出错 - 直接用
childNodes会包含空白文本节点,需要额外判断tagName,增加了不必要的复杂度 - 封装函数时崩溃大概率是因为变量作用域(比如
var导致的变量泄漏)或者DOM操作的错误(比如原代码里items.insertBefore(input, ulChild.childNodes);的节点引用错误)
下面是一步步的优化方案:
1. 封装通用工具函数
先把重复用到的逻辑抽成独立函数,复用性更强:
查找联系人
用数组的find方法替代手动for循环,更简洁直观:
// 根据firstName查找通讯录中的联系人 function getContactByFirstName(firstName) { return contactsBook.find(contact => contact.firstName === firstName); }
获取联系人列表项
用querySelectorAll直接获取UL下的所有LI元素,不用手动遍历子节点:
// 获取指定UL下的所有联系人信息LI项 function getContactListItems(ulElement) { return ulElement.querySelectorAll('li'); }
2. 拆分独立的按钮处理函数
把Edit、Save、Delete的逻辑拆成单独的函数,职责单一,可读性拉满:
处理编辑操作
function handleEditContact(ul, button) { const contact = getContactByFirstName(ul.dataset.person); if (!contact) return; const listItems = getContactListItems(ul); listItems.forEach(item => { // 创建输入框并填充当前文本 const input = document.createElement('input'); input.type = 'text'; input.value = item.textContent; // 替换LI内容为输入框 item.textContent = ''; item.appendChild(input); }); // 切换按钮文本为Save button.textContent = 'Save'; }
处理保存操作
function handleSaveContact(ul, button) { const contact = getContactByFirstName(ul.dataset.person); if (!contact) return; const listItems = getContactListItems(ul); // 假设你的LI顺序对应firstName, lastName, phone等字段,可根据实际调整 contact.firstName = listItems[0].querySelector('input').value; contact.lastName = listItems[1].querySelector('input').value; contact.phone = listItems[2].querySelector('input').value; // 更新LI显示为新的文本 listItems.forEach((item, index) => { const fieldValue = Object.values(contact)[index]; item.textContent = fieldValue; }); // 切换按钮文本为Edit button.textContent = 'Edit'; // 更新localStorage localStorage.setItem('addbook', JSON.stringify(contactsBook)); }
处理删除操作
function handleDeleteContact(ul) { const contact = getContactByFirstName(ul.dataset.person); if (!contact) return; // 从DOM中移除联系人UL ul.remove(); // 从通讯录数组中移除对应项 const contactIndex = contactsBook.indexOf(contact); contactsBook.splice(contactIndex, 1); // 更新localStorage localStorage.setItem('addbook', JSON.stringify(contactsBook)); }
3. 重构点击事件监听
现在主点击事件就变得非常简洁,只负责分发操作:
// Click event for Edit and Delete buttons. contacts.addEventListener("click", (e) => { if (e.target.tagName !== "BUTTON") return; const button = e.target; const ul = button.parentNode; switch(button.textContent) { case "Edit": handleEditContact(ul, button); break; case "Save": handleSaveContact(ul, button); break; case "Delete": handleDeleteContact(ul); break; } });
关键优化点说明
- 消除重复逻辑:所有遍历查找的逻辑都被抽成工具函数,避免了多次写相同的for循环和if判断
- 简化DOM操作:用
querySelectorAll替代childNodes,自动过滤非LI节点,减少冗余判断 - 职责单一:每个按钮操作都有独立函数,代码逻辑清晰,后期维护或修改某功能时不用动其他部分
- 修复潜在错误:原代码中
items.insertBefore(input, ulChild.childNodes);的节点引用错误被修正为直接将输入框追加到LI中,避免DOM操作报错
内容的提问来源于stack exchange,提问作者Darsh2987




