前端 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 ...
随机推荐
- Educational Codeforces Round 160 (Rated for Div. 2)
A 直接模拟,注意细节 #include<bits/stdc++.h> #define ll long long using namespace std; ll p[15] = {1}; ...
- Solution Set - NOI级别真题选做
[NOI2007] 社交网络 Link&Submission. key:Floyd Floyd求出任意两点间最短路,以及最短路的条数.求点 \(k\) 的答案时枚举所有点对 \(i,j\),若 ...
- HTTPS 是如何进行安全传输的 ?
概述 现代密码学对信息的处理主要离不开以下的三种形式: 摘要:主要用于数据校验,例如存储密码等,摘要是对信息进行单向的哈希,改变信息的原有形态,因为哈希函数的特点是易变性(即使微小的变化也会产生完全不 ...
- 自定义Lua解析器管理器-------演化脚本V0.5
[3]自定义Lua解析器管理器-------演化脚本V0.5 方便我们在项目中使用Lua解析方法,我们封装管理一个lua解析器,管理LuaState的方法执行. 解析器脚本: using LuaInt ...
- ansible系列(33)--ansible实战之部署WEB集群架构(3)
目录 1. 应用环境部署 1.1 nginx编译部署 1.2 PHP编译部署 1.3 mariadb二级制部署 1.4 redis部署 1.5 NFS部署 1.6 keepalived+LVS部署 1 ...
- WEB服务与NGINX(21)- nginx 的fastcgi反向代理功能
目录 1. NGINX实现fastcgi反向代理 1.1 fastcgi概述 1.2 nginx实现fastcgi相关参数 1.3 nginx与php-fpm部署在一台服务器 1.3.1 php服务器 ...
- 稳定、省钱的 ClickHouse 读写分离方案:基于 JuiceFS 的主从架构实践
Jerry 是一家位于北美的科技公司,利用人工智能和机器学习技术,简化汽车保险和贷款的比价和购买流程.在美国,Jerry 的应用在其所属领域排名第一. 随着数据规模的增长,Jerry 在使用 AWS ...
- CSS动画-数字轮盘滚动效果实现(组件封装,快速使用)
效果图: 原理分析:这玩意就和垂直方向的轮播图差不多,只是把轮播的图换成数字 主要实现:父组件:父组件接收一个curNum属性,这个属性代表当前需要显示的数字.它将这个数字传递给子组件AnimateN ...
- Linux 提权-Sudo_1
本文通过 Google 翻译 Sudo Part-1 – Linux Privilege Escalation - Juggernaut-Sec 这篇文章所产生,本人仅是对机器翻译中部分表达别扭的字词 ...
- Biwen.Settings添加对IConfiguration&IOptions的集成支持
Biwen.Settings 是一个简易的配置项管理模块,主要的作用就是可以校验并持久化配置项,比如将自己的配置存储到数据库中,JSON文件中等 使用上也是很简单,只需要在服务中注入配置即可, 比如我 ...