【问题标题】:Two arrays cloned with Lodash, separate but the same. Why?使用 Lodash 克隆的两个数组,分开但相同。为什么?
【发布时间】:2019-05-22 21:46:24
【问题描述】:

事实上,我知道这里有一些非常明显的东西我完全遗漏了,因此非常感谢您的帮助。

我有一个提供两个下拉菜单的功能。它们包含相同的数据(该功能允许两个人之间进行交易,人就是数据),但我希望他们每个人都获得自己的数据副本。

此功能的另一部分是通过在第一个下拉列表中选择人 A,我想在第二个下拉列表中禁用人 A,反之亦然,所以我有 ng-options 标记注意 disabled 属性在物体上。

我遇到的问题是,即使使用 Lodash 的 clone 之类的方法在第一次分配时正确创建一个“新”数组,每次我在一个数组中访问人 A 时(特别是不要访问另一个array) 总是我看到当我触摸 Person A 时,该对象在两个数组中都更新了,这让我很慌张。

这感觉像是一个彻头彻尾的、准系统的 Javascript 问题(标准 PEBCAK,我觉得我显然误解了或直接遗漏了一些基本的东西),也许有一点 AngularJS 渲染相关的乐趣参与其中,但是...什么给出了?

angular.module('myApp', [])
  .controller('weirdDataController', function($scope) {
    $scope.$watch('manager1_id', () => {
      if (angular.isDefined($scope.manager1_id) && parseInt($scope.manager1_id, 10) > 0) {
        $scope._disableManagerInOtherDropdown(false, $scope.manager1_id);
      }
    });

    $scope.$watch('manager2_id', () => {
      if (angular.isDefined($scope.manager2_id) && parseInt($scope.manager2_id, 10) > 0) {
        $scope._disableManagerInOtherDropdown(true, $scope.manager2_id);
      }
    });

    $scope._gimmeFakeData = () => {
      return [{
          manager_id: 1,
          manager_name: 'Bill',
          disabled: false
        },
        {
          manager_id: 2,
          manager_name: 'Bob',
          disabled: false
        },
        {
          manager_id: 3,
          manager_name: 'Beano',
          disabled: false
        },
        {
          manager_id: 4,
          manager_name: 'Barf',
          disabled: false
        },
        {
          manager_id: 5,
          manager_name: 'Biff',
          disabled: false
        },
      ];
    };

    const data = $scope._gimmeFakeData();
    $scope.firstManagers = _.clone(data);
    $scope.secondManagers = _.clone(data);

    $scope._disableManagerInOtherDropdown = (otherIsFirstArray, managerId) => {
      const disableManagers = manager => {
        manager.disabled = manager.manager_id === managerId;
      };

      if (otherIsFirstArray) {
        $scope.firstManagers.forEach(disableManagers);
      } else {
        $scope.secondManagers.forEach(disableManagers);
      }

      console.log('Is the first item the same??', $scope.firstManagers[0].disabled === $scope.secondManagers[0].disabled);
    }
  });
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.5/angular.min.js"></script>
<div ng-app="myApp" ng-controller="weirdDataController">
  <div class="col-xs-12 col-sm-6">
    <select class="form-control" ng-model="manager1_id" ng-options="manager.manager_id as manager.manager_name disable when manager.disabled for manager in firstManagers track by manager.manager_id">
      <option value="" disabled="disabled">Choose one manager</option>
    </select>
    <select class="form-control" ng-model="manager2_id" ng-options="manager.manager_id as manager.manager_name disable when manager.disabled for manager in secondManagers track by manager.manager_id">
      <option value="" disabled="disabled">Choose another manager</option>
    </select>
  </div>
</div>
<br /><br />

我把所有相关的东西都放在$scope 上,只是为了让它工作并说明问题,但事情是这样的:

  • 在初始化时,我抓取数组,然后为每个下拉列表克隆一个副本
  • 当每个下拉列表更改模型属性(对象 ID)时,我有一个范围侦听器,然后调用一个方法来处理禁用 OPOSITE 列表中选定的对象/人员
  • 在此方法中,我确定要遍历两个列表/数组中的哪一个并标记禁用的对象
  • 在此方法结束时,我执行了一个简单的 console.log 调用来检查给定对象的值。为了快速简单起见,我只抓取索引 0 处的项目。
  • 我所期望的:一个对象的disabled 值为true,而另一个对象的值为false。我所看到的:他们都有true(假设您选择下拉列表中的第一个“真实”项目)

有什么关系?我到底有多白痴?

【问题讨论】:

  • 您已经复制了数组,但它们都引用了相同的对象实例。因此,在一个数组中改变对象将导致另一个数组中的相同突变,因为它们是对相同对象的引用。
  • 我是否从根本上误解了clone 的工作原理?当我第一次启动这个功能时,我确实只是在做简单的赋值,所以它是一个共享数组,但后来我开始使用clone 来解决这个问题。让我再去看看文档看看。
  • 是的,你想要_.cloneDeep()
  • 顺便说一句,我认为帕特里克在这里一针见血,虽然 cloneDeep 解决了问题,但问题最终是在功能布局方面有点代码味道.我备份并重新考虑了该功能的设计方式(我自己,4 年前?)并相应地对其进行了更新,以更好地处理 UI 和底层数据之间的分离。感谢所有的帮助!
  • @Mattygabe,如果您自己发布一个答案会很有用,这样这个问题......好吧,有一个答案;-) 回答您自己的问题是完全可以的。跨度>

标签: javascript arrays angularjs ecmascript-6 lodash


【解决方案1】:

我的问题的答案是:clone() 默认情况下不执行“深度”克隆,因此我处理的是同一个数组,尽管我做了有缺陷的尝试。使用 Lodash 的 cloneDeep() 方法解决了我的问题,但正如 Patrick 建议的那样,我重新评估了我如何编写有问题的方法并对其进行了重构,我完全不需要使用任何克隆。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-03-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多