函数意外将数组自身作为元素嵌套,求问题原因
问题
我实现了一个根据用户选择创建N个输入字段的函数,后续需要获取这些输入值用于处理,但该函数会反复将返回的数组自身作为元素添加到数组中,形成无限嵌套的结构。只有在返回时添加conj.pop()才能解决此问题,我希望了解其原因。
控制台输出的数组示例如下:
Array(3)
0: 'A'
1: 'B'
2: (3) ['A','B','Array(3)]
展开该数组后会无限重复此结构。
测试输入为:输入2、2,然后分别输入A B、C D。
相关代码如下:
JavaScript 代码
document.getElementById("ok1").onclick = () => { const esp_qtd = document.getElementById("esp_qtd").value; const car_qtd = document.getElementById("car_qtd").value; document.getElementById("parte1").classList.add("invisivel"); document.getElementById("parte2").classList.remove("invisivel"); console.log(esp_qtd, car_qtd); const generateFields = (tipo) => { const qtd = document.getElementById(tipo + "_qtd").value; const parent = document.getElementById(tipo + "_lista"); for (let i = 0; i < qtd; i++) { const input = document.createElement("input"); input.setAttribute("type", "text"); input.setAttribute("id", (tipo + i)); parent.appendChild(input); if (qtd > 5) { if (((i + 1) % 3) == 0) parent.appendChild(document.createElement("br")); } console.log(i); } } generateFields("esp"); generateFields("car"); const inputFields = (tipo, conj) => { const qtd = document.getElementById(tipo + "_qtd").value; for (let i = 0; i < qtd; i++) { conj[i] = document.getElementById(tipo + i).value; console.log("Iteration: " + i, conj); } return conj; } document.getElementById("ok2").onclick = () => { const conjE = []; const conjC = []; conjE.push(inputFields("esp", conjE)); conjC.push(inputFields("car", conjC)); console.log(conjE); console.log(conjC); } }
CSS 代码
* { font-family: 'Roboto', sans-serif; font-size: 14pt; margin-top: 1rem; } .invisivel { display: none; } label { margin-top: 1rem; } input { margin-top: 0.5rem; margin-right: 1rem; margin-bottom: 0.5rem; } button { margin-top: 1rem; }
HTML 代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>DOC</title> <link rel="stylesheet" href="style.css"> <link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link href="https://fonts.googleapis.com/css2?family=Roboto&display=swap" rel="stylesheet"> </head> <body> <div class="container"> <!-- PART 1 --> <div id="parte1"> <form> <label>N1</label><br> <input type="text" id="esp_qtd"><br> <label>N2</label><br> <input type="text" id="car_qtd"><br> <button id="ok1" type="button">OK</button> </form> </div> <!-- PART 2 --> <div id="parte2" class="invisivel"> <div id="esp_lista"> <label>ELEMENTS 1</label><br> </div> <div id="car_lista"> <label>ELEMENTS 2</label><br> </div> <button id="ok2" type="button">OK</button> </div> </div> <script src="index.js"></script> </body> </html>
原因分析
问题核心出在这两行代码的逻辑错误:
conjE.push(inputFields("esp", conjE)); conjC.push(inputFields("car", conjC));
1. 数组的引用特性
JavaScript里数组是引用类型,你把conjE传给inputFields函数时,函数操作的是数组本身,而不是它的副本。在inputFields内部,你已经把输入值依次赋值给了conj的索引项(比如conj[0] = 'A'、conj[1] = 'B'),这时候conjE已经是['A', 'B']了。
2. push操作的叠加问题
当你调用conjE.push(inputFields("esp", conjE))时,inputFields会返回conjE本身(函数最后return conj;),这行代码等价于:
// 先执行inputFields,把conjE填充为['A','B'] inputFields("esp", conjE); // 再把conjE自己推到自己里面 conjE.push(conjE);
这就导致conjE变成了['A', 'B', conjE],因为数组是引用类型,最后一个元素就是数组本身,展开时自然会无限嵌套。
3. 为什么conj.pop()能临时解决
你在inputFields返回前加conj.pop(),本质是把刚才push进去的数组自身给移除了,刚好修正了错误操作带来的额外元素,但这是个绕弯的临时方案。
正确的写法
根本不需要用push,因为inputFields已经直接修改了传入的数组,直接调用函数即可:
document.getElementById("ok2").onclick = () => { const conjE = []; const conjC = []; inputFields("esp", conjE); inputFields("car", conjC); console.log(conjE); // 输出 ['A','B'] console.log(conjC); // 输出 ['C','D'] }
或者更合理的写法是让inputFields自己创建数组并返回,避免依赖外部传入的数组,逻辑更清晰:
const inputFields = (tipo) => { const qtd = document.getElementById(tipo + "_qtd").value; const conj = []; for (let i = 0; i < qtd; i++) { conj[i] = document.getElementById(tipo + i).value; } return conj; } // 调用时直接接收返回值 document.getElementById("ok2").onclick = () => { const conjE = inputFields("esp"); const conjC = inputFields("car"); console.log(conjE); console.log(conjC); }
内容的提问来源于stack exchange,提问作者ashalcath

