求助:传入generateBulkInsertQueries函数后rowsx变量为何被覆盖?
问题:传入函数后rowsx变量被覆盖的原因分析
有人能解释一下,当rowsx变量传入generateBulkInsertQueries函数后为何会被覆盖吗?我期望在整个代码执行过程中,rowsx的长度始终为1。
相关代码如下:
//HELPER FUNCTIONS------------------------------------------- var generateBulkInsertQueries = ( data, dbname, tablename, colnames, batchSize=10 ) => { if ([dbname, tablename].some((element) => element.indexOf('..') > -1)) throw `generateInsertQueries() expected distinct db (${dbname}) and tables (${tablename})`; //if the colnames param is passed use it, otherwise generate colnames form the data if (!dbname || dbname.length < 1) throw `generateInsertQueries(): invalid dbname (${dbname})`; let cols = colnames ? colnames : Object.keys(data[0]); //transform keys are column names //transform values are inserted strings let values = []; for(let rows of splitArrayIntoChunks(data,batchSize)) { let ss=rows.map(r=>`(${sqlConcatOneLine(r,cols)})`) values.push({outdata:`insert into [${dbname}].[dbo].[${tablename}](${cols.map((c) => `[${c}]`).join(',')}) values ${ss.join(',')}`,indata:rows}); }; return values }; //demo: let [list,chunkSize] = [[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15], 6]; var splitArrayIntoChunks=(list,chunkSize)=>{ list = [...Array(Math.ceil(list.length / chunkSize))].map(_ => list.splice(0,chunkSize)) //console.log(list); return list } var sqlConcatOneLine=(row,cols)=>{ return cols .map((k) => { let ret = "''"; try { ret = "'" + row[k].toString().replace(/'/g, '').replace(/\n/g, '').replace(/\r/g, '').toString() + "'"; } catch (e) { // console.log( // `WARN: err inserting value (likely null) generateSQLBulkInsertQuery: ${e}` // ); } return ret; }) .join(',') } //EXAMPLE OF ERROR STARTS HERE-------------------------------- (async ()=>{ //init the var var rowsx = [{ col1: "1", col2: "2" }]; console.log({rowsxlen:rowsx.length,here:1}) var sqliq = generateBulkInsertQueries(rowsx, 'xx', 'yy', ['col1','col2'],1); console.log({rowsxlen:rowsx.length,here:2}) if(rowsx.length!=1) console.log(`failed. rowsx.length!=1`) else console.log(`success!!!!`) })()
原因分析
- 数组是引用类型:JavaScript中数组属于引用类型,当你把rowsx传入
generateBulkInsertQueries函数时,传递的是数组的引用而非副本。函数内部对数组的修改会直接作用于外部的rowsx变量。 - splice方法直接修改原数组:问题核心在
splitArrayIntoChunks函数里的list.splice(0,chunkSize)语句。splice是会直接修改原数组的方法,它会从原数组中删除指定数量的元素并返回这些元素。调用该方法时,传入的rowsx数组会被直接修改,元素被移除后长度变为0。
解决方案
要避免原数组被修改,需在分割前创建原数组的副本,再对副本操作,有两种常见方式:
方式一:复制原数组后使用splice
修改splitArrayIntoChunks函数,先创建数组副本,避免修改原引用:
var splitArrayIntoChunks=(list,chunkSize)=>{ const arrCopy = [...list]; // 用扩展运算符创建数组副本 return [...Array(Math.ceil(arrCopy.length / chunkSize))].map(_ => arrCopy.splice(0,chunkSize)) }
方式二:使用slice方法(无原数组修改)
slice方法不会修改原数组,而是返回新子数组,用它分割更安全:
var splitArrayIntoChunks=(list,chunkSize)=>{ const chunks = []; for (let i = 0; i < list.length; i += chunkSize) { chunks.push(list.slice(i, i + chunkSize)); } return chunks; }
修改后,外部的rowsx数组不会被函数内部操作修改,长度将保持为1。
内容的提问来源于stack exchange,提问作者Rilcon42
相关产品推荐
相关产品推荐

