这样的主次分明的对比,大大提高了underscore的工作效率。所以说每一个小小的细节,都可以体现出作者的处心积虑。阅读源码,能够使我们学习到太多的东西。
5 underscore的缺陷之处
我们可以在其他方法中看到underscore对ES6中新特征的支持,比如_.is[Type]方法已经支持检测Map(_.isMap)和Set(_.isSet)等类型了。但是_.isEqual却没有对Set和Map结构的支持。如果我们使用_.isEqual比较两个Map或者两个Set,总是会得到true的结果,因为它们可以通过所有的检测。
在underscore的官方GitHub repo上,我看到有同学已经提交了PR添加了_.isEqual对Set和Map的支持。
我们可以看一下源码:
var size = a.size; // Ensure that both objects are of the same size before comparing deep equality. if (b.size !== size) return false; while (size--) { // Deep compare the keys of each member, using SameValueZero (isEq) for the keys if (!(isEq(a.keys().next().value, b.keys().next().value, aStack, bStack))) return false; // If the objects are maps deep compare the values. Value equality does not use SameValueZero. if (className === '[object Map]') { if (!(eq(a.values().next().value, b.values().next().value, aStack, bStack))) return false; } }
可以看到其思路如下:
1 比较两参数的长度(或者说是键值对数),长度不一者即为不等,返回false。
2 如果长度相等,就逐一递归比较它们的每一项,有任意一项不等者就返回false。
3 全部通过则可以认为是相等的,返回true。
这段代码有一个很巧妙的地方在于它没有区分到底是Map对象还是Set对象,先直接使用a.keys().next().value以及b.keys().next().value获取Set的元素值或者Map的键。后面再进行类型判断,如果是Map对象的话,再使用a.values().next().value以及b.values().next().value获取Map的键值,Map对象还需要比较其键值是否相等。
个人认为,这段代码也有其局限性,因为Set和Map可以认为是一个数据集,这区别于数组对象。我们可以说[1,2,3]不等于[2,1,3],因为其相同元素的位置不同;但是我认为new Set([1,2,3])应该认为等于new Set([2,1,3]),因为Set是无序的,它内部的元素具有单一性。