前端 Array.sort() 源码学习
源码地址
V8源码Array
710行开始为sort()相关
Array.sort()方法是那种排序呢?
去看源码主要是源于这个问题
// In-place QuickSort algorithm.
// For short (length <= 22) arrays, insertion sort is used for efficiency.
源码中的第一句话就回答了我的问题
// 通常使用快速排序算法
// 如果数组长度小于23,则插入排序效率更好
既然都打开了,索性就看看源码叭,看看sort到底做了些啥
我把一整坨源码码分成一块一块来看,让自己比较清晰的知道sort到底干了些啥,下面是阅读代码时,自己的思路梳理
第一块代码
if (!IS_CALLABLE(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;
};
}
第一块内容判断,如果传进来的参数不可回调,则给一个默认的回调函数
这个回调函数,判断俩值是不是Smi
// Smi:小整数(Small integers)V8引擎中的元素类型之一
`https://medium.com/@justjavac/v8-internals-how-small-is-a-small-integer-ba5e17a3ae5f`
// PS: markdown语法有问题,这里直接贴出 url
如果是则进行小整数字典序比较
什么是字典序
否则将两个值转换成字符串进行字符串比较大小
字符串如何比较大小
第二块代码
var InsertionSort = function InsertionSort(a, from, to) {
...
};
var QuickSort = function QuickSort(a, from, to) {
if (to - from <= 10) {
InsertionSort(a, from, to);
return;
}
...
};
第二块就是正常的快速排序和插入排序
这里采取的是数量小于10的数组使用 InsertionSort(插入),比10大的数组则使用 QuickSort(快速)。
第三块代码
if (!is_array) {
// For compatibility with JSC, we also sort elements inherited from
// the prototype chain on non-Array objects.
// We do this by copying them to this object and sorting only
// own elements. This is not very efficient, but sorting with
// inherited elements happens very, very rarely, if at all.
// The specification allows "implementation dependent" behavior
// if an element on the prototype chain has an element that
// might interact with sorting.
max_prototype_element = CopyFromPrototype(array, length);
}
这块代码里面的注释,讲的还是比较详细的,百度翻译也非常nice
// 为了与JSC兼容,我们还在非数组对象上对从原型链继承的元素进行排序。
// 我们通过将它们复制到这个对象并只对自己的元素排序来实现这一点。
// 这不是很有效,但是使用继承的元素进行排序的情况很少发生,如果有的话。
// 如果原型链上的元素具有可能与排序交互的元素,则规范允许“依赖于实现”的行为。
第四块代码
// Copy elements in the range 0..length from obj's prototype chain
// to obj itself, if obj has holes. Return one more than the maximal index
// of a prototype property.
var CopyFromPrototype = function CopyFromPrototype(obj, length) {
var max = 0;
for (var proto = %object_get_prototype_of(obj);
proto;
proto = %object_get_prototype_of(proto)) {
var indices = IS_PROXY(proto) ? length : %GetArrayKeys(proto, length);
if (IS_NUMBER(indices)) {
// It's an interval.
var proto_length = indices;
for (var i = 0; i < proto_length; i++) {
if (!HAS_OWN_PROPERTY(obj, i) && HAS_OWN_PROPERTY(proto, i)) {
obj[i] = proto[i];
if (i >= max) { max = i + 1; }
}
}
}
else {
for (var i = 0; i < indices.length; i++) {
var index = indices[i];
if (!HAS_OWN_PROPERTY(obj, index) && HAS_OWN_PROPERTY(proto, index)) {
obj[index] = proto[index];
if (index >= max) { max = index + 1; }
}
}
}
}
return max;
};
这块代码是对于非数组的一个处理
注释里面说到
// 如果obj有holes(能猜出大概意思,不咋好翻译这个hole)
// 就把obj原型链上0-length所有元素赋值给obj本身
// 返回一个max,max是比原型属性索引最大值+1
返回的max会在下面用到
第五块代码
if (!is_array && (num_non_undefined + 1 < max_prototype_element)) {
// For compatibility with JSC, we shadow any elements in the prototype
// chain that has become exposed by sort moving a hole to its position.
ShadowPrototypeElements(array, num_non_undefined, max_prototype_element);
}
注释翻译:
// 为了与JSC兼容
// 我们对原型链中通过sort将一个hole移动到其位置而暴露的所有元素
// 进行shadow处理。
可能因为英语语法水平不够,单看注释还有点不明白
大致意思是,把“掀开的东西,再盖上”
直接看下面一块代码,看看这个shadow操作到底干了啥叭
第六块代码
// Set a value of "undefined" on all indices in the range from..to
// where a prototype of obj has an element. I.e., shadow all prototype
// elements in that range.
var ShadowPrototypeElements = function(obj, from, to) {
for (var proto = %object_get_prototype_of(obj); proto;
proto = %object_get_prototype_of(proto)) {
var indices = IS_PROXY(proto) ? to : %GetArrayKeys(proto, to);
if (IS_NUMBER(indices)) {
// It's an interval.
var proto_length = indices;
for (var i = from; i < proto_length; i++) {
if (HAS_OWN_PROPERTY(proto, i)) {
obj[i] = UNDEFINED;
}
}
}
else {
for (var i = 0; i < indices.length; i++) {
var index = indices[i];
if (from <= index && HAS_OWN_PROPERTY(proto, index)) {
obj[index] = UNDEFINED;
}
}
}
}
};
这块代码就是shadow操作,注释翻译如下:
// 在范围从..到obj原型包含元素的所有索引上设置一个“undefined”值。
// 换句话说
// 在该范围内对所有原型元素进行shadow处理。
其中:
I.e.是拉丁文id est 的缩写,它的意思就是“那就是说,换句话说”
英文不够你用了是不你还要写拉丁文?!
果然大致的意思猜的没错
在刚刚把对象的原型属性的复制,现在要设置undefined来shadow他了
第七块代码
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);
}
意思是 数组中有索引访问器。使用JS函数将数组hole和未定义项移到末尾,该函数在访问器存在时是安全的。
下面是安全移出数组hole方法
var SafeRemoveArrayHoles = function SafeRemoveArrayHoles(obj) {
// Copy defined elements from the end to fill in all holes and undefineds
// in the beginning of the array. Write undefineds and holes at the end
// after loop is finished.
var first_undefined = 0;
var last_defined = length - 1;
var num_holes = 0;
while (first_undefined < last_defined) {
// Find first undefined element.
while (first_undefined < last_defined &&
!IS_UNDEFINED(obj[first_undefined])) {
first_undefined++;
}
// Maintain the invariant num_holes = the number of holes in the original
// array with indices <= first_undefined or > last_defined.
if (!HAS_OWN_PROPERTY(obj, first_undefined)) {
num_holes++;
}
// Find last defined element.
while (first_undefined < last_defined &&
IS_UNDEFINED(obj[last_defined])) {
if (!HAS_OWN_PROPERTY(obj, last_defined)) {
num_holes++;
}
last_defined--;
}
if (first_undefined < last_defined) {
// Fill in hole or undefined.
obj[first_undefined] = obj[last_defined];
obj[last_defined] = UNDEFINED;
}
}
// If there were any undefineds in the entire array, first_undefined
// points to one past the last defined element. Make this true if
// there were no undefineds, as well, so that first_undefined == number
// of defined elements.
if (!IS_UNDEFINED(obj[first_undefined])) first_undefined++;
// Fill in the undefineds and the holes. There may be a hole where
// an undefined should be and vice versa.
var i;
for (i = first_undefined; i < length - num_holes; i++) {
obj[i] = UNDEFINED;
}
for (i = length - num_holes; i < length; i++) {
// For compatability with Webkit, do not expose elements in the prototype.
if (i in %object_get_prototype_of(obj)) {
obj[i] = UNDEFINED;
} else {
delete obj[i];
}
}
// Return the number of defined elements.
return first_undefined;
};
还会判断数组长度
if (length < 2) return array;
前端 Array.sort() 源码学习的更多相关文章
- [算法1-排序](.NET源码学习)& LINQ & Lambda
[算法1-排序](.NET源码学习)& LINQ & Lambda 说起排序算法,在日常实际开发中我们基本不在意这些事情,有API不用不是没事找事嘛.但必要的基础还是需要了解掌握. 排 ...
- Underscore.js 源码学习笔记(下)
上接 Underscore.js 源码学习笔记(上) === 756 行开始 函数部分. var executeBound = function(sourceFunc, boundFunc, cont ...
- 【Vue源码学习】响应式原理探秘
最近准备开启Vue的源码学习,并且每一个Vue的重要知识点都会记录下来.我们知道Vue的核心理念是数据驱动视图,所有操作都只需要在数据层做处理,不必关心视图层的操作.这里先来学习Vue的响应式原理,V ...
- 【 js 基础 】【 源码学习 】源码设计 (持续更新)
学习源码,除了学习对一些方法的更加聪明的代码实现,同时也要学习源码的设计,把握整体的架构.(推荐对源码有一定熟悉了之后,再看这篇文章) 目录结构:第一部分:zepto 设计分析第二部分:undersc ...
- Underscore.js 源码学习笔记(上)
版本 Underscore.js 1.9.1 一共 1693 行.注释我就删了,太长了… 整体是一个 (function() {...}()); 这样的东西,我们应该知道这是一个 IIFE(立即执行 ...
- 【 js 基础 】【 源码学习 】源码设计 (更新了backbone分析)
学习源码,除了学习对一些方法的更加聪明的代码实现,同时也要学习源码的设计,把握整体的架构.(推荐对源码有一定熟悉了之后,再看这篇文章) 目录结构:第一部分:zepto 设计分析 第二部分:unders ...
- SpringBoot源码学习系列之SpringMVC自动配置
目录 1.ContentNegotiatingViewResolver 2.静态资源 3.自动注册 Converter, GenericConverter, and Formatter beans. ...
- 源码学习VUE之Observe
在文章 源码学习VUE之响应式原理我们大概描述了响应式的实现流程,主要写了observe,dep和wather的简易实现,以及推导思路.但相应代码逻辑并不完善,今天我们再来填之前的一些坑. Obser ...
- 【Vue源码学习】依赖收集
前面我们学习了vue的响应式原理,我们知道了vue2底层是通过Object.defineProperty来实现数据响应式的,但是单有这个还不够,我们在data中定义的数据可能没有用于模版渲染,修改这些 ...
- [数据结构1.2-线性表] 动态数组ArrayList(.NET源码学习)
[数据结构1.2-线性表] 动态数组ArrayList(.NET源码学习) 在C#中,存在常见的九种集合类型:动态数组ArrayList.列表List.排序列表SortedList.哈希表HashTa ...
随机推荐
- kali 的 vim 中不能粘贴复制
kali 的 vim 中不能粘贴复制 进入 vim 命令行模式,输入 :set mouse=c 之后可以正常粘贴复制
- gorm使用小结
增 db.Create(user) db.Save(user) 参数只能用**结构体指针****,因为要根据指针写入该条插入的数据, 所以user可以作为该条数据使用. 新增只能用结构体 save方法 ...
- 03. x86基础指令
[说明] x86指令代码语法 制作程序时,指令数据使用代码表示,这些指令代码称为汇编代码,汇编代码由汇编器转换为对应的指令数据和数学数据. x86指令代码主要有两种语法:英特尔语法.AT&T语 ...
- 瑞亚时间管理大师,基于 .NET 6 和 Angular 构建的在线任务管理协作平台
瑞亚时间管理大师 瑞亚时间管理大师, 是一个在线的任务管理.项目管理. 团队协作平台.瑞亚 拥有现代化的页面风格,高效.简便,同时适合个人和团队使用. 瑞亚对个人免费,提供了无限制的任务,列表,和空间 ...
- Ubuntu 启用交换分区
前言 交换分区也称之为 swap 分区,允许系统在内存不足的情况下将内存程序写入文件,防止系统卡死失去响应的情况发生. 检查现有交换分区 首先,确认系统中是否已存在交换分区或文件.在终端中输入以下命令 ...
- 4G EPS 第四代移动通信系统
目录 文章目录 目录 4G EPS 4G EPS 4G(the 4th generation mobile communication technology,第四代移动通信技术)提供了 3G 不能满足 ...
- mybatis-puls解决多数据源事务的问题
直接上代码: pom: <!--JTA组件核心依赖--> <dependency> <groupId>org.springframework.boot</gr ...
- c#获取开机时间
public static DateTime OpenCom() { TimeSpan t = TimeSpan.FromMilliseconds(System.Environment.TickCou ...
- Opencv笔记(12)傅里叶变换
在之前了解的OpenCV为我们实现的图像变换,这些本质上是从图像到输出图像的映射,即输入仍是一幅图像.本章的傅里叶变换,输出数组的值在含义上和原图像的强度值大不相同,是输入图像的频域表示. cv::d ...
- 01-布局扩展-用calc来计算实现双飞翼布局
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8 ...