# 数组中的空值_浅谈 JavaScript 中 sort( ) 排序的坑

762343f0f95726e6c0b841df896751c2.png

Array 数组的排序受关注度一直就不高,除非当它出现了问题。最近我在项目中就遇到了一个数组排序问题,数组中的项没有按我预想的被排序,导致界面上无法正常显示。我花了很长的时间才明白问题究竟出在哪里,所以想在这里和大家分享一下。

基本排序

JS 中的 Array 对象有一个排序方法 sort( ),调用它的运行结果一般来说是这样的:

afbd07652f7fd49d1ab50fc85bb682ee.png

甚至当数组中出现未定义值的元素时 sort( ) 也同样好用。文档中有这样一句话:

“所有未定义的数组元素都会被转化成字符串,并通过比较 UTF-16 的值来进行排序。”https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort
8212f0b0b141b0d1799c891395f731e0.png

一些陷阱

使用排序时你可能会遇到的第一个问题就是当数组中有 null 时。排序方法会强行把空值转换成字符串"null",而 n 位于字母表大概中间的一个位置。

cdfde3af7bb1c0697149c0ad1151bf24.png

然后就是数字。排序方法的默认算法是将数组内全部元素转成一个字符串数组,再比较它们的 UTF-16 编码。如之前所见,在元素为字符串时这个方法很好用,但碰见数字元素时就尴尬了。

aca580c813c447323932ebf8e0d9f4f3.png

如上图所示,在这个例子中运行结果里 10 排在 3 之前,因为字符串"10"排在字符串"3"之前。

我们可以通过给 JS 提供一个用于排序的比较函数来解决这个问题。函数从数组对象中接收两个元素并返回一个数字的值,该值是大于,小于或是等于零决定了这两个数字排序时的位置。比方说如果返回值为负数,第一个元素就会被排在第二个元素之前,如果值为0则证明两个元素等值。

要升序排列这些数字,函数的写法很简单。用两个元素相减,符合我们的描述,最终也会得出正确的排序。我们把这个函数提供给 JS 排序的方法,和下图中一样:

b0f2cead29fb72af86603bbe8138c6c1.png
9674508cd274dd6065031e0bdb6fc2ef.png

注意这个解决方案同样适用于当有元素未定义时,因为未定义元素默认排在末尾。

5c9991ac2ddfbe9e78af520fd837b34e.png

不过 null 仍然是一个问题。

58c28b5c68c88c5cd114436fa9aea196.png

因为将 null 放入数字转换方法中得到的是 0。

6b253c2066a53c73cc9a3d62fd665d29.png

你可以通过再次改进之前所写的 compareNumbers 函数,或者勉强继续使用。

排序结果不一致性

最大的问题,也就是最近我才发现的问题,就是当 undefined 未定义以其它的方式进入到数组中。之前我们已经知道如果有未定义的元素,它默认会被排到末尾。然而如果被排序的对象,键是未定义的话,出现的结果就会不一致。

比方说,你有一个对象数组,其中一些对象有值,一些对象没有,排序的结果可能与你的预期产生偏差。

da4da478f947550b3823c1f4caee0cf7.png

用 undefined 减去一个数字或者用数字减去 undefined 都会返回 NaN,NaN 并不在数字比较方法返回值的范围中,因为它或为正或为负或等于0,所以最后的排序结果会很奇怪。本例中,引发问题的元素位置不变,该元素上下的数字仅仅被局部排序。

这个问题的解决方法有几个,但重要的就是我们需要知道它是有可能发生的。在我的项目中,我直接过滤掉了没有值的数组元素。

9409f0b3fd887295e2e806dc135e54c3.png

结论

上文分析的结果就在于 sort( ) 排序方法并不如我们所想象的那么直接和易用。字符串排序没有问题,数字排序需要额外的操作但也能实现,虽然方法本身对 undefined 已经有处理方法,但我们必须格外小心对 undefined 和 null 对象的强制转型。

微信搜索 “ng_4bytes” 关注4字节官方微信号,获取IT界一手资讯,更有免费程序员培训课程等你来拿!

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值