微信小程序开发陷阱:数组引用类型导致的数据同步修改问题

位置:首页 / 技术中心 / 杂项

杂项 Admin 2026-01-23 14:46:06 44

微信小程序开发陷阱:数组引用类型导致的数据同步修改问题

问题背景

在开发微信小程序时,我遇到了一个看似诡异的问题:明明已经将完整的标记数组保存到了 markersAll 中,结果打印出来却发现数据少了。具体表现为:

  • 从接口获取到 3 条打卡记录数据
  • 将这些数据转换为地图标记数组 markers (包含 3 个标记)
  • 把 markers 赋值给 markersAll 并打印
  • 对 markers 进行裁剪操作(只保留起点和终点)
  • 再次打印 markersAll ,发现它也变成了只有 2 个标记!

问题原因

JavaScript 引用类型的特性

问题的根源在于 JavaScript 中数组是引用类型 。当执行 this.setData({ markersAll: markers }) 时, 并不是创建了一个新的数组副本 ,而是将 markers 数组的引用地址赋值给了 markersAll 。

这意味着 markersAll 和 markers 指向内存中的同一个数组对象。因此,当后续执行 markers.splice() 操作修改 markers 时, markersAll 自然也会同步变化。

代码重现

// 1. 从接口获取数据并转换为标记数组
let markers = res.data.list.map(item => ({
  id: Date.now(),
  latitude: item.latitude,
  longitude: item.longitude,
  iconPath: null,
  width: 20,
  height: 20,
  anchor: { x: 0.5, y: 0.5 }
}));
// 此时 markers 有 3 个元素

// 2. 保存到 markersAll
this.setData({
  markersAll: markers  // 这里只是传递引用,不是拷贝
});
console.log(this.data.markersAll.length); // 打印 3

// 3. 修改 markers
markers.splice(1, 1); // 删除中间元素
console.log(this.data.markersAll.length); // 打印 2!被意外修改了

解决方案

使用深拷贝创建独立副本

要解决这个问题,需要在赋值时创建数组的 深拷贝 ,确保 markersAll 和 markers 指向不同的数组对象。
方法:JSON 序列化/反序列化
这是一种简单有效的深拷贝方法,适用于大多数场景:

// 修改前
this.setData({
  markersAll: markers
});

// 修改后
const markersAll = JSON.parse(JSON.stringify(markers));
this.setData({
  markersAll
});

原理

  1. JSON.stringify(markers) 将数组转换为 JSON 字符串
  2. JSON.parse(...) 将 JSON 字符串解析回新的数组对象
  3. 这样就创建了一个完全独立的数组副本,修改 markers 不会影响 markersAll

代码优化建议

1. 明确分离数据用途

在处理数据时,应根据不同用途创建独立的数据副本:

  • markersAll :存储完整的标记数据
  • markers :用于地图显示(可能需要裁剪或处理)

2. 选择合适的拷贝方法

  • 浅拷贝:适用于简单数组(元素为基本类型)
    const copy = [...original]; // 扩展运算符
    const copy = original.slice(); // slice 方法
  • 深拷贝:适用于复杂数组(元素为对象)
    // 方法1:JSON 序列化(推荐)
    const copy = JSON.parse(JSON.stringify(original));
    
    // 方法2:递归拷贝(适用于包含函数等特殊类型的情况)
    function deepClone(obj) {
      if (obj === null || typeof obj !== 'object') return obj;
      if (obj instanceof Array) return obj.map(item => deepClone(item));
      const clonedObj = {};
      for (const key in obj) {
        if (obj.hasOwnProperty(key)) {
          clonedObj[key] = deepClone(obj[key]);
        }
      }
      return clonedObj;
    }

3. 避免频繁修改数组

在小程序开发中,频繁修改数组并调用 setData 会影响性能。建议:

  • 先在内存中完成所有数据处理
  • 最后一次性调用 setData 更新界面

实际效果

修改后,代码执行流程变为:

  1. 创建原始 markers 数组(3 个元素)
  2. 深拷贝创建 markersAll (独立数组,3 个元素)
  3. 裁剪 markers 数组(变为 2 个元素)
  4. 打印 markersAll ,仍然是 3 个元素(数据保持完整)

总结

这个问题提醒我们:在 JavaScript 开发中, 引用类型的赋值操作需要特别小心 。尤其是在处理数组、对象等复杂数据结构时,要明确是否需要创建副本,避免因引用传递导致的数据意外修改。

通过使用深拷贝创建独立的数据副本,可以有效避免这类问题,让代码逻辑更清晰、更可预测。这不仅适用于微信小程序开发,也是前端开发中的通用最佳实践。

关键点回顾

  • 核心问题:JavaScript 中数组是引用类型,直接赋值仅传递内存地址,修改原数组会同步影响赋值后的变量。
  • 解决方案:使用深拷贝(JSON 序列化/反序列化、递归拷贝)创建独立数组副本,浅拷贝仅适用于元素为基本类型的简单数组。
  • 优化建议:分离不同用途的数据副本,减少小程序中 setData 的调用频次,提升性能和代码可维护性。
18455111359 扫描二维码