Node.js中循环更新var变量时const值意外变更问题排查
Node.js中const变量为何随var变量修改而变化?
在Node.js的订单更新接口中,我从req.body解构出orderState和updatedOrder用于更新MongoDB记录,同时将req.body.updatedOrder赋值给const modifiedOrder,计划后续传给Neo4j执行同步更新。但在for循环中修改updatedOrder的cart项状态为'Refunded'后,发现modifiedOrder的对应值也被修改,导致传给Neo4j的cart项状态不再是初始的'Refunded requested',想知道为什么const变量的值会随var变量的修改而变化。
代码片段
var { orderState, updatedOrder } = req.body; const modifiedOrder = req.body.updatedOrder; console.log( '1> Mongoose updatedOrder received modifiedOrder.cart is', modifiedOrder.cart ); // correct if (orderState) { ... } else if (updatedOrder) { for (var i = 0; i < updatedOrder.cart.length; i++) { console.log( `LOOP> Mongoose updatedOrder before loop ${i} modifiedOrder.cart is ${modifiedOrder.cart}` ); // 第一次循环(i=0):第一个item状态为"Refund requested",正确 // 第二次循环(i=1):第一个item状态变为"Refunded",错误 var cartItem = JSON.parse(updatedOrder.cart[i]); if (cartItem.state == 'Refund requested') { if (cartItem.isPromotion == false) { await Product.findByIdAndUpdate( cartItem.id, { $inc: { soldQuantity: -1, availableQuantity: 1 }, }, { session: session, new: true, }, null ) .clone() .exec() .then((result) => { console.log( 'Mongoose Order.updateOrder / Product.update Inventory updated successfully for product:', result.id ); if (result != null) { cartItem.state = 'Refunded'; totalRefundAmount += result.price; } else { cartItem.state = 'Not refunded'; } return; }) .catch((error) => { if (error) { console.log( 'Mongoose Order.updateOrder / Product.update error: ', error ); cartItem.state = 'Not refunded'; throw error; } }); } else if (cartItem.isPromotion == true) { await Promotion.findByIdAndUpdate( cartItem.id, { $inc: { soldQuantity: -1, availableQuantity: 1 }, }, { session: session, new: true, } ) .clone() .exec() .then((result) => { if (result != null) { totalRefundAmount += result.price; cartItem.state = 'Refunded'; console.log( 'Mongoose Order.updateOrder / Promotion.update Inventory updated successfully for promotion:', result ); } else { cartItem.state = 'Not refunded'; } return; }) .catch((error) => { if (error) { console.log( 'Mongoose Order.updateOrder / Promotion.update error: ', error ); cartItem.state = 'Not refunded'; throw error; } }); } } updatedOrder.cart[i] = JSON.stringify(cartItem); } ... }
日志信息
循环前日志(第一个item状态为"Refund requested")
LOOP> Mongoose updatedOrder before loop 0 modifiedOrder.cart is {"createdOnDate":1638894572905,"name":"testProduct","brand":"someBrand","price":12,"description":"description","category":"Safety and locks","city":"Bologna","region":"Emilia-Romagna","country":"Italy","vendor":"testShop","vendorId":"OrderTestShop","barcode":"some","imageUrl":"https://firebasestorage.googleapis.com/v0/b/fix-it-b4b00.appspot.com/o/Products%2F61af8bec02edbe24ce034963?alt=media&token=a891dc05-407e-43d2-ab2b-0f49226249a9","fullImages":[],"thumbNails":[],"minimumStock":10,"availableQuantity":10,"soldQuantity":0,"isPromotion":false,"totalRating":0,"ratings":0,"_id":"636a0c9e8145999716d71de2","createdAt":"2022-11-08T08:00:30.399Z","updatedAt":"2022-11-08T08:00:30.399Z","__v":0,"averageRating":0,"id":"636a0c9e8145999716d71de2","isRecommendation":true,"state":"Refund requested"},{"createdOnDate":1638894572905,"productId":"636a0c9e8145999716d71de2","name":"testPromotion","brand":"someBrand","price":12,"description":"new description","category":"Safety and locks","city":"Bologna","region":"Emilia-Romagna","country":"Italy","vendor":"testShop","vendorId":"OrderTestShop","barcode":"some","imageUrl":"https://firebasestorage.googleapis.com/v0/b/fix-it-b4b00.appspot.com/o/Products%2F61af8bec02edbe24ce034963?alt=media&token=a891dc05-407e-43d2-ab2b-0f49226249a9","fullImages":[],"thumbNails":[],"minimumStock":10,"availableQuantity":10,"soldQuantity":0,"isPromotion":true,"totalRating":0,"ratings":0,"_id":"636a0c9e8145999716d71de4","createdAt":"2022-11-08T08:00:30.467Z","updatedAt":"2022-11-08T08:00:30.467Z","__v":0,"averageRating":0,"id":"636a0c9e8145999716d71de4","isRecommendation":true,"state":"Bought"}
第一次循环后日志(第一个item状态变为"Refunded")
LOOP> Mongoose updatedOrder before loop 1 modifiedOrder.cart is {"createdOnDate":1638894572905,"name":"testProduct","brand":"someBrand","price":12,"description":"description","category":"Safety and locks","city":"Bologna","region":"Emilia-Romagna","country":"Italy","vendor":"testShop","vendorId":"OrderTestShop","barcode":"some","imageUrl":"https://firebasestorage.googleapis.com/v0/b/fix-it-b4b00.appspot.com/o/Products%2F61af8bec02edbe24ce034963?alt=media&token=a891dc05-407e-43d2-ab2b-0f49226249a9","fullImages":[],"thumbNails":[],"minimumStock":10,"availableQuantity":10,"soldQuantity":0,"isPromotion":false,"totalRating":0,"ratings":0,"_id":"636a0c9e8145999716d71de2","createdAt":"2022-11-08T08:00:30.399Z","updatedAt":"2022-11-08T08:00:30.399Z","__v":0,"averageRating":0,"id":"636a0c9e8145999716d71de2","isRecommendation":true,"state":"Refunded"},{"createdOnDate":1638894572905,"productId":"636a0c9e8145999716d71de2","name":"testPromotion","brand":"someBrand","price":12,"description":"new description","category":"Safety and locks","city":"Bologna","region":"Emilia-Romagna","country":"Italy","vendor":"testShop","vendorId":"OrderTestShop","barcode":"some","imageUrl":"https://firebasestorage.googleapis.com/v0/b/fix-it-b4b00.appspot.com/o/Products%2F61af8bec02edbe24ce034963?alt=media&token=a891dc05-407e-43d2-ab2b-0f49226249a9","fullImages":[],"thumbNails":[],"minimumStock":10,"availableQuantity":10,"soldQuantity":0,"isPromotion":true,"totalRating":0,"ratings":0,"_id":"636a0c9e8145999716d71de4","createdAt":"2022-11-08T08:00:30.467Z","updatedAt":"2022-11-08T08:00:30.467Z","__v":0,"averageRating":0,"id":"636a0c9e8145999716d71de4","isRecommendation":true,"state":"Bought"}
问题原因
这是JavaScript中引用类型赋值的特性导致的:
updatedOrder和modifiedOrder都是指向req.body.updatedOrder这个同一个对象的引用,不是独立的副本。const关键字的作用是限制变量本身不能被重新赋值(比如不能写modifiedOrder = {}),但完全允许修改它指向的对象的属性或内部数据。- 当你修改
updatedOrder.cart里的元素时,实际上是在修改内存中同一个对象的内容,所以modifiedOrder作为同一个对象的引用,自然会看到变化。
解决方案
要保存req.body.updatedOrder的初始状态,需要创建一个深拷贝,让modifiedOrder指向一个完全独立的新对象。最简单的实现方式是用JSON序列化再反序列化:
const modifiedOrder = JSON.parse(JSON.stringify(req.body.updatedOrder));
这样modifiedOrder就是一个全新的对象,后续修改updatedOrder的内容不会影响到它。
内容的提问来源于stack exchange,提问作者Vincenzo
相关产品推荐
相关产品推荐

