当前位置:首页 > 日记 > 正文

通过V8源码看一个关于JS数组排序的诡异问题

通过V8源码看一个关于JS数组排序的诡异问题

前言

前几天一个朋友在微信里面问我一个关于 JS 数组排序的问题。通过该问题发现了一些之前没发现的内容,下面话不多少了,来一起看看详细的介绍吧。

原始数组如下:

var data = [ {value: 4},  {value: 2},  {value: undefined},  {value: undefined},  {value: 1},  {value: undefined},  {value: undefined},  {value: 7},  {value: undefined},  {value: 4}];

data 是个数组,数组的每一项都是一个拥有 value 作为 key 的对象,值为数字或者 undefined。

data .sort((x, y) => x.value - y.value) .map(x => x.value);

对数组的 value 进行排序,然后把排完序的数组进行 flat 处理。得到的结果如下:

[2, 4, undefined, undefined, 1, undefined, undefined, 7, undefined, 4]

显然这没有达到我们的目的。

现在我们修改一下排序,挑战一下函数的调用顺序:先对数组进行扁平化(flat)处理,然后再排序。

data .map(x => x.value) .sort((x, y) => x - y)

这时我们得到的结果和之前截然不同:

[1, 2, 4, 4, 7, undefined, undefined, undefined, undefined, undefined]

遇到这种情况第一感觉肯定是要去看看 ECMA 规范,万一是 JS 引擎的 bug 呢。

在 ES6 规范 22.1.3.24 节写道:

Calling comparefn(a,b) always returns the same value v when given a specific pair of values a and b as its two arguments. Furthermore, Type(v) is Number, and v is not NaN. Note that this implies that exactly one of a < b, a = b, and a > b will be true for a given pair of a and b.

简单翻译一下就是:第二个参数 comparefn 返回一个数字,并且不是 NaN。一个注意事项是,对于参与比较的两个数 a 小于 b、a 等于 b、a 大于 b 这三种情况必须有一个为 true。

所以严格意义上来说,这段代码是有 bug 的,因为比较的结果出现了 NaN。

在 MDN 文档上还有一个细节:

如果 comparefn(a, b) 等于 0, a 和 b 的相对位置不变。备注:ECMAScript 标准并不保证这一行为,而且也不是所有浏览器都会遵守。

翻译成编程术语就是:sort 排序算法是不稳定排序。

其实我们最疑惑的问题上,上面两行代码为什么会输出不同的结果。我们只能通过查看 V8 源码去找答案了。

V8 对数组排序是这样进行的:

如果没有定义 comparefn 参数,则生成一个(高能预警,有坑啊):

comparefn = function (x, y) { if (x === y) return 0; if (%_IsSmi(x) && %_IsSmi(y)) { return %SmiLexicographicCompare(x, y); } x = TO_STRING(x); // <----- 坑 y = TO_STRING(y); // <----- 坑 if (x == y) return 0; else return x < y ? -1 : 1;};

然后定义了一个插入排序算法:

function InsertionSort(a, from, to) { for (var i = from + 1; i < to; i++) { var element = a[i]; for (var j = i - 1; j >= from; j--) {  var tmp = a[j];  var order = comparefn(tmp, element);  if (order > 0) { // <---- 注意这里  a[j + 1] = tmp;  } else {  break;  } } a[j + 1] = element;}

为什么是插入排序?V8 为了性能考虑,当数组元素个数少于 10 个时,使用插入排序;大于 10 个时使用快速排序。

后面还定义了快速排序函数和其它几个函数,我就不一一列出了。

函数都定义完成后,开始正式的排序操作:

// %RemoveArrayHoles returns -1 if fast removal is not supported.var num_non_undefined = %RemoveArrayHoles(array, length);if (num_non_undefined == -1) { // There were indexed accessors in the array. // Move array holes and undefineds to the end using a Javascript function // that is safe in the presence of accessors. num_non_undefined = SafeRemoveArrayHoles(array);}

中间的注释:Move array holes and undefineds to the end using a Javascript function。排序之前会把数组里面的 undefined 移动到最后。因此第二个排序算法会把 undefined 移动到最后,然后对剩余的数据 [4,2,1,7,4] 进行排序。

而在第一种写法时,数组的每一项都是一个 Object,然后最 Object 调用 x.value - y.value 进行计算,当 undefined 参与运算时比较的结果是 NaN。

当返回 NaN 时 V8 怎么处理的呢?我前面标注过,再贴一次:

var order = comparefn(tmp, element);if (order > 0) { // <---- 这里 a[j + 1] = tmp;} else { break;}

NaN > 0 为 false,执行了 else 分支代码。

思考题,以下代码的结果:

[1, 23, 2, 3].sort()

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对的支持。

相关文章

Photoshop 7.0新增了哪些功能?

Photoshop 7.0新增了哪些功能?

功能,新增了,电脑软件,Photoshop,Photoshop 7.0 上市好久了 ,当时它也是现在的CC 版本一样 独一无二的, 想知道当年PS&ldquo;进化&rdquo;的过程的同学跟我来吧!介绍你们当时新增的功能。软件名称:Adobe PhotoShop7.0 简体中文版软件大小:154MB更…

Excel中表格工作表保护密码撤销的

Excel中表格工作表保护密码撤销的

密码,操作方法,表格,工作,操作步骤,  在做excel表格时我们有时候会给自己的工作表加密,一旦忘记密码就会很麻烦,如何绕过密码,直接撤销保护。今天,小编就教大家在Excel中表格工作表保护密码撤销的操作方法。Excel中表格工作表保护密码撤销的…

jquery实现回车键触发事件 | 实例

jquery实现回车键触发事件 | 实例

回车键,事件,实例,电脑软件,jquery,键盘事件有3:keydown,keypress,keyup,分别是按下,按着没上抬,上抬键盘 。正确代码为:$(document).keyup(function(event){ if(event.keyCode ==13){ $("#submit").trigger("click"); }});推荐:keyup,防止笔记…

浅谈JavaScript find 方法不支持IE

浅谈JavaScript find 方法不支持IE

方法,不支持,浅谈,电脑软件,find,最近在前端开发中,遇到一个JavaScript 的问题。<script type="text/javascript"> var arrayList = new Array(); arrayList.push(1); arrayList.push(2); arrayList.push(3); arrayList.push(4); a…

怎样利用成熟的字体排版规则创造令

怎样利用成熟的字体排版规则创造令

字体,网页,创造,惊艳,成熟,漂亮的字体排版总能为网站设计加分不少。别具一格的设计虽然精彩,但是更多的时候,沿着前人探索出来的设计规则来设计,会更加得心应手。怎样利用成熟的规则来创造令人惊艳的网页呢?今天的文章就沿着这样的思路来探索网…

详解JavaScript按概率随机生成事件

详解JavaScript按概率随机生成事件

事件,概率,详解,电脑软件,JavaScript,最近做了一个JavaScript按概率随机生成事件,于是整理了一下思路,写了一个小demo:/**在抽奖的活动中经常会用到这个算法,不同奖项的获取概率不同,要按概率去随机生成对应的奖品**/function random(arr1, arr2…

详解——不让Tomcat重启

详解——不让Tomcat重启

重启,详解,电脑软件,Tomcat,要做到这样功能需要对本机有所配置一下:安装步骤:1、 在 windows 启动安装程序,在控制台输入 :java -jar dcevm-0.2-win.jar (路径放到dcevm-0.2-win.jar的文件夹)下面附件下载等一会儿,这时会出现一个程序框。选择一…

win10回收站自动清空

win10回收站自动清空

回收站,清空,电脑软件,回收站基本上是历代Windows桌面的必备摆设,它的作用就是防止用户误删除文件后没地儿哭去,属于&ldquo;后悔药&rdquo;&hellip;&hellip;不过对于某些&ldquo;系统洁癖&rdquo;用户来说,看回收站图标被一堆&ldquo;废纸&rdquo;…

jQuery is not defined 错误原因与

jQuery is not defined 错误原因与

解决方法,错误,原因,电脑软件,jQuery,通常出现这种状况有几种解决方法:1:查看是否引入jquery文件就算引入了文件了是不是通过一些整站下载器之类的软件下载的,都会出现问题,建议用迅雷到官方下载即可。2:查询路径是否错误,可以在页面源码中点…

Ai怎么制作一种文字镶嵌的效果?

Ai怎么制作一种文字镶嵌的效果?

文字,效果,电脑软件,Ai,平时使用AI软件来设计,若是直接写文字,有时候看起来就会显得很单调、乏味,若是能来点创意的设计,肯定能让人充满新鲜感,眼前一亮,比如把一张自己喜欢的,漂亮的图片填充嵌入到文字里面,又会是怎么样的感觉呢?让文字拥有了图片…

WPS文字中怎么给文章添加水印

WPS文字中怎么给文章添加水印

文字,水印,文章,电脑软件,WPS,  在文档中添加水印可以说是一种标志与象征,我们可以再很多文件中看到添加水印的形式,如&ldquo;机密文件&rdquo;、&ldquo;严禁复制&rdquo;等等,添加水印现在来说是一种趋势,更是一种&ldquo;维权&rdquo;的象征。…

Excel中出现循环引用警告的解决方

Excel中出现循环引用警告的解决方

循环引用,步骤,解决方法,电脑软件,Excel,  很多人在打开Excel的时候会频繁弹出&ldquo;循环引用警告&rdquo;这是怎么回事?是什么原因造成的?如何阻止弹出呢?今天,小编就教大家在Excel中出现循环引用警告的解决方法。Excel中出现循环引用警…