乐趣区

js数组sort方法的坑

背景

sort 方法用来对数组元素进行排序。但是浏览器实现可能有不同,已经两次踩到坑了,因此记录一下。

规范

ecma-262 制定的排序规则:
使用两个参数 x 和 y 唤起排序
1、如果 x、y 都是 undefined,返回 +0
2、如果 x 为 undefined,返回 1
3、如果 y 未 undefined,返回 -1
4、如果定义了排序方法
a、设 v = ToNumber(comparefn(x, y))
b、如果 v 是 NaN,返回 +0
c、返回 v
5、设 xString = ToString(x)
6、设 yString = ToString(y)
7、如果 xString < yString,返回 -1
8、如果 yString < xString,返回 1
9、返回 +0

返回 1 的时候,x 应排在 y 后面
返回 0 的时候,x、y 位置不变
返回 - 1 的时候,x 应排在 y 前面

坑 1:排序不一定是稳定的,即相等的两个元素不一定会保持在原来的位置。排序方法返回 0 的两个元素仍可能交换位置,如 chrome 浏览器的排序实现,在数组长度小于等于 10 的时候就使用插入排序法,没有什么问题,但是数组大于 10 的时候就会使用快速排序,在比较起始值和中值的时候,排序方法返回值大于和等于 0 的时候都会对调位置。
我遇到的情况是,需要对会员卡数组进行两个维度的比较排序,数组元素对象可能包含次卡或者折扣属性,次卡排在折扣卡前面,折扣卡按折扣大小排序。当时我写的排序方法为:(a, b) => {return (a.discount && b.times) || a.discount > b.discount },后来发现有时次卡没有排在折扣卡前面,改为:(a, b) => {if ((a.discount && b.times) || a.discount > b.discount) return 1; return -1; })

坑 2:如上面排序规则第 4 条 a,按标准需要把排序方法返回值转为 Number 类型,但是有些浏览器版本如 Safari10 并没有实现这一步,如果排序方法返回 true/false 布尔值而不是 Number 类型,元素位置就不会进行调整。

退出移动版