Array 数组的排序受关注度一直就不高,除非当它出现了问题。最近我在项目中就遇到了一个数组排序问题,数组中的项没有按我预想的被排序,导致界面上无法正常显示。我花了很长的时间才明白问题究竟出在哪里,所以想在这里和大家分享一下。
基本排序
JS 中的 Array 对象有一个排序方法sort( ),调用它的运行结果一般来说是这样的:
甚至当数组中出现未定义值的元素时sort( )也同样好用。文档中有这样一句话:
“所有未定义的数组元素都会被转化成字符串,并通过比较 UTF-16 的值来进行排序。”
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort
一些陷阱
使用排序时你可能会遇到的第一个问题就是当数组中有null时。排序方法会强行把空值转换成字符串"null",而 n 位于字母表大概中间的一个位置。
然后就是数字。排序方法的默认算法是将数组内全部元素转成一个字符串数组,再比较它们的 UTF-16 编码。如之前所见,在元素为字符串时这个方法很好用,但碰见数字元素时就尴尬了。
如上图所示,在这个例子中运行结果里 10 排在 3 之前,因为字符串"10"排在字符串"3"之前。
我们可以通过给 JS 提供一个用于排序的比较函数来解决这个问题。函数从数组对象中接收两个元素并返回一个数字的值,该值是大于,小于或是等于零决定了这两个数字排序时的位置。比方说如果返回值为负数,第一个元素就会被排在第二个元素之前,如果值为0则证明两个元素等值。
要升序排列这些数字,函数的写法很简单。用两个元素相减,符合我们的描述,最终也会得出正确的排序。我们把这个函数提供给 JS 排序的方法,和下图中一样:
注意这个解决方案同样适用于当有元素未定义时,因为未定义元素默认排在末尾。
不过null仍然是一个问题。
因为将null放入数字转换方法中得到的是 0。
你可以通过再次改进之前所写的compareNumbers函数,或者勉强继续使用。
排序结果不一致性
最大的问题,也就是最近我才发现的问题,就是当undefined未定义以其它的方式进入到数组中。之前我们已经知道如果有未定义的元素,它默认会被排到末尾。然而如果被排序的对象,键是未定义的话,出现的结果就会不一致。
比方说,你有一个对象数组,其中一些对象有值,一些对象没有,排序的结果可能与你的预期产生偏差。
用 undefined 减去一个数字或者用数字减去undefined都会返回NaN,NaN并不在数字比较方法返回值的范围中,因为它或为正或为负或等于0,所以最后的排序结果会很奇怪。本例中,引发问题的元素位置不变,该元素上下的数字仅仅被局部排序。
这个问题的解决方法有几个,但重要的就是我们需要知道它是有可能发生的。在我的项目中,我直接过滤掉了没有值的数组元素。
结论
上文分析的结果就在于sort( )排序方法并不如我们所想象的那么直接和易用。字符串排序没有问题,数字排序需要额外的操作但也能实现,虽然方法本身对undefined已经有处理方法,但我们必须格外小心对undefined和null对象的强制转型。
领取专属 10元无门槛券
私享最新 技术干货