前端 Array.sort() 源码学习

news2024/11/15 7:18:37

源码地址

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;

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1870012.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Python第三方库GDAL 安装

安装GDAL的方式多种&#xff0c;包括pip、Anaconda、OSGeo4W等。笔者在安装过程中&#xff0c;唯独使用pip安装遇到问题。最终通过轮子文件&#xff08;.whl&#xff09;成功安装。 本文主要介绍如何下载和安装较新版本的GDAL轮子文件。 一、GDAL轮子文件下载 打开Github网站…

【Java Web】Element-plus组件库

目录 一、Element-plus组件库概述 二、Element-plus组件库基本用法 一、Element-plus组件库概述 Element-plus组件库是由饿了么团队基于Vue3框架编写的前端UI设计组件库。通俗点讲就是将用户页面设计所需的按钮、表格、导航栏等前端代码编写生成的组件元素都封装好了、用户在进…

40.设计HOOK引擎的好处

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 上一个内容&#xff1a;39.右键弹出菜单管理游戏列表 以 39.右键弹出菜单管理游戏列表 它的代码为基础进行修改 效果图&#xff1a; 实现步骤&#xff1a; 首…

elk对于集群实例的日志的整合-基于logstash采集日志

说明&#xff1a;基于logstash采集日志 环境&#xff1a; 物理机192.168.31.151 一.启动2个测试实例&#xff0c;每5-10s随机生成一条订单日志 实例一 包位置&#xff1a;/home/logtest/one/log-test-0.0.1-SNAPSHOT.jar 日志位置:/docker/elastic/logstash_ingest_data/l…

《2024年新生代妈妈真实孕育状态洞察报告》

专注于行业分析与市场研究的专业机构易观分析,正式发布了其最新研究成果——《2024年新生代妈妈真实孕育状态洞察报告》。该报告深入探讨了新生代妈妈在孕育过程中的实际需求与挑战,通过对母婴行业的市场规模、消费行为、用户触媒习惯、用户关怀以及特定品类场景的细致分析,揭示…

easyui的topjui前端框架使用指南

博主今天也是第一次点开easyui的商业搜权页面&#xff0c;之前虽然一直在使用easyui前端框架&#xff08;easyui是我最喜欢的前端ui框架&#xff09;&#xff0c;但是都是使用的免费版。 然后就发现了easyui的开发公司居然基于easyui开发出了一个新的前端框架&#xff0c;于是我…

PLM系统选购指南:哪款品牌最适合你?

在选购PLM&#xff08;Product Lifecycle Management&#xff09;系统时&#xff0c;选择最适合自己企业的品牌至关重要。以下是一份清晰的PLM系统选购指南&#xff0c;帮助您根据企业的具体需求选择最合适的品牌&#xff1a; 1、明确企业需求&#xff1a; 首先&#xff0c;明…

数学建模--lingo解决线性规划问题~~灵敏度分析的认识

目录 1.线性规划问题举隅 &#xff08;1&#xff09;问题介绍 &#xff08;2&#xff09;问题分析 &#xff08;3&#xff09;灵敏度分析 &#xff08;4&#xff09;方法缺陷 &#xff08;5&#xff09;可行域&凸集 2.lingo的简单认识 &#xff08;1&#xff09;默认…

VSCode常用的一些插件

Chinese (Simplified) 汉语&#xff08;简体&#xff09;拓展包。 Auto Close Tag 可以自动增加xml/html的闭合标签。 CodeSnap 截图神器。截图效果在下面。 Dracula Official vscode一个很好看的主题。 Git Graph git管理工具。 GitHub Repositories 有了它&#xff0c;不…

C++02 变量和基本类型

基本类型 字、字节、bit、Byte之间的关系 字 word 字节 Byte 位 bit 1字 2字节 <---> 1word 2Byte 1字节 8位 <---> 1Byte 8bit 1Byte 8bits 1KB 1024Bytes 1MB 1024KB 1GB 1024MB #include <iostream> using namespace std; int main() {/*字符…

手把手教你打造高精度STM32数字时钟,超详细步骤解析

STM32数字时钟项目详解 1. 项目概述 STM32数字时钟是一个集成了时间显示、闹钟功能、温湿度检测等多功能于一体的小型电子设备。它利用STM32的实时时钟(RTC)功能作为核心,配合LCD显示屏、按键输入、温湿度传感器等外设,实现了一个功能丰富的数字时钟系统。 2. 硬件组成 STM…

腾讯云对象存储cors错误处理

最近将公司的域名进行了修改&#xff0c;同时将腾讯云的对象存储改成了https&#xff0c;为了安全嘛。然后上传软件包的时候发现上传软件就失败了。 在浏览器中打开该 HTML 文件&#xff0c;单击 Test CORS 发送请求后&#xff0c;出现以下错误&#xff0c;错误提示&#xff1…

【Java Web】PostMan业务接口测试工具

目录 一、PostMan概述 二、如何安装Postman 三、Postman的基本使用 一、PostMan概述 在生产环境中&#xff0c;一个项目在开发之前、前后端开发工程师通常需要商讨在前后端数据交互时需要采用什么样的规范格式&#xff0c;如&#xff1a;前端向后端发送请求的uri、请求和响…

【索引】数据库索引之散列索引

目录 1、什么是散列&#xff1f; 2、如何评价一个散列函数的好坏&#xff1f; 3、散列中的桶溢出处理 4、散列在索引中的应用 4、顺序索引和散列索引的比较 1、什么是散列&#xff1f; 顺序文件组织的一个缺点是我们必须访问索引结构来定位数据&#xff0c;或者必须使用二…

datax入门(datax的安装与简单使用)——01

datax入门&#xff08;datax的安装与简单使用&#xff09;——01 1. 官网2. 工具部署&#xff08;通过下载DataX工具包&#xff09;2.1 下载、解压2.2 配置2.2.1 查看配置模版2.2.2 根据模版配置json2.2.3 启动DataX 3. datax的简单使用3.1 mysql2stream3.2 mysql2mysql3.2.1 拼…

HTML【重点标签】

一、列表标签 1.无序列表 父级别&#xff1a; 无序列表的标题 ----表示无序列表的整体&#xff0c;用于包裹li标签 子级别&#xff1a; 无序列表一行的内容 ----表示无序列表的每一项&#xff0c;用于包含一行的内容 语义&#xff1a;构建没有顺序的列表 特点&#xff1a;列…

php聚合快递寄快递小程序

一、引言&#xff1a;告别传统寄件&#xff0c;拥抱便捷新选择 在数字化时代&#xff0c;我们越来越追求便捷和高效。传统的寄件方式已经无法满足现代人快速、便捷的需求。因此&#xff0c;一款聚合快递优惠寄件小程序应运而生&#xff0c;它集合了多家快递公司&#xff0c;为…

Linux高级编程——进程

1.进程的含义? 进程是一个程序执行的过程&#xff0c;会去分配内存资源&#xff0c;cpu的调度 PID, 进程标识符 当前工作路径 chdir umask 0002 进程打开的文件列表 文件IO中有提到 &#xff08;类似于标准输入 标准输出的编号&#xff0c;系统给0&#xff0c;1&#xf…

台灯的功能作用有哪些?分享好用的护眼灯!看完就知道台灯怎么选

在当今时代&#xff0c;学生们长时间地沉浸于平板、手机、电脑等电子设备中&#xff0c;这些设备的屏幕往往伴随着频闪和蓝光辐射&#xff0c;这无疑对视力健康构成了潜在威胁。家长们日益关注孩子的护眼养眼问题&#xff0c;因为视力疲劳和眼部疾病不仅会降低个体的生活质量&a…

Hyperf 在 NginxProxyManager 如何配置 websocket?

新建代理 填写域名等服务信息&#xff0c;选择支持WebSockets。 创建 SSL 编写nginx配置 location /message.io{proxy_pass http://<你的ip>:<对应端口号>;proxy_http_version 1.1;proxy_set_header Upgrade $http_upgrade;proxy_set_header Connection "Upg…