AngularJS 1.4 迁移 - ngOptions:是否已通过损坏的对象相等性检查进行跟踪?

AngularJS 1.4 migration - ngOptions: Has track by broken object equality checks?

我希望你们能帮助我阐明自从我们从 Angular 1.3 迁移到 Angular 1.4 以来我一直面临的问题。

我已经创建了一个支持 JSFiddle 演示来演示这个

说明

我有一个包含两个 MyElement 实例列表的控制器,用户可以 select 使用 <select>.

还有一个按钮,用于比较 (===) selected MyElement 与列表中的第一个元素。此比较的结果记录到控制台。

<select>使用track by element.id来区分MyElement-实例。

奇怪的是 selected 项从不 等于列表的第一个元素(即使第一个元素是 select编)。绑定到 ctrl.selectedElement 的对象是不同的对象,但具有相同的属性:

NO!
Object {id: 1, description: "First"}
MyElement {id: 1, description: "First"}

当我删除 track by element.id 返回到 Angular 1.3 时,此行为消失(两个元素严格相等,正如我所期望的那样是)。

问题

这是一个错误吗?这是否与 fix(ngOptions) 有关,但我不知何故遗漏了其中的含义?这是怎么回事?

提前致谢!

JavaScript 中对象的默认相等运算符在它们引用内存中的相同位置时产生 true。更新必须以不遵守先前声明的方式更改对象。查看 migration docs 以查看更改是否记录在案。虽然,我可以看到 track by 确实在创建一个新对象——这将是虚假 === check

的症结所在

为此使用 angular.equals,而不是 ===。此检查有一些细节,如 angular.equals docs...

中所述

Two objects or values are considered equivalent if at least one of the following is true:

  • Both objects or values pass === comparison.
  • Both objects or values are of the same type and all of their properties are equal by comparing them with angular.equals.
  • Both values are NaN. (In JavaScript, NaN == NaN => false. But we consider two NaN as equal)
  • Both values represent the same regular expression (In JavaScript, /abc/ == /abc/ => false. But we consider two regular expressions as equal when their textual representation matches).

During a property comparison, properties of function type and properties with names that begin with $ are ignored.

Scope and DOMWindow objects are being compared only by identify (===).

// -- 'YES!'
console.log(angular.equals(this.selectedElement, this.elements[0]) ? 'YES!' : 'NO!'); 

JSFiddle Link - 更新演示


尝试 中提供可能的最佳答案,我在 v1.4.0 中挖掘了 ngOptions 的源代码,track by 的以下代码似乎返回一个新对象,从而解释以上所有内容。

// Get the value by which we are going to track the option
// if we have a trackFn then use that (passing scope and locals)
// otherwise just hash the given viewValue
var getTrackByValueFn = trackBy ?
    function(value, locals) { return trackByFn(scope, locals); } :
    function getHashOfValue(value) { return hashKey(value); };

var getTrackByValue = function(value, key) {
    return getTrackByValueFn(value, getLocals(value, key));
};

var locals = {}; // ---------- <<<<<<<<<<<<<<<< ---------- new object
var getLocals = keyName ? function(value, key) {
    locals[keyName] = key;
    locals[valueName] = value;
    return locals;
} : function(value) {
    locals[valueName] = value;
    return locals;
};

使用JSON.stringify然后比较。不知道用起来效率高不高

var MyElement = function (id, description) {
    this.id = id;
    this.description = description;
}

var MyController = function () {
    this.elements = [
        new MyElement(1, 'First'),
        new MyElement(2, 'Second')
    ];
};

MyController.prototype.isFirst = function (element) {
    console.log(JSON.stringify(this.selectedElement) === JSON.stringify(this.elements[0]) ? 'YES!' : 'NO!');  
    console.log(this.selectedElement);
    console.log(this.elements[0]);
};

angular
    .module('myApp', [])
    .controller('MyController', MyController);