You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 22:05:29