前端 Array.sort() 源碼學習

来源:https://www.cnblogs.com/xw-01/p/18266079
-Advertisement-
Play Games

源碼地址 V8源碼Array 710行開始為sort()相關 Array.sort()方法是那種排序呢? 去看源碼主要是源於這個問題 // In-place QuickSort algorithm. // For short (length <= 22) arrays, insertion 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;

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • ‍ 寫在開頭 點贊 + 收藏 學會 場景是用戶通過微信掃app內的收款碼,跳到一個h5頁面。然後完成支付。 代碼實現的整體流程: 使用微信掃碼,碼是app內生成的,碼的內容是一個h5頁面鏈接,掃碼完成後跳轉到自定義的h5支付界面。 掃碼進入後,將頁面展示所需要的參數進行緩存起來, ...
  • 摘要:本文詳細介紹了Nuxt3中的六個核心生命周期鉤子及其用法,包括build:done、build:manifest、builder:generateApp、builder:watch、pages:extend和server:devHandler:handler。內容涵蓋各鉤子的調用時機、參數、環... ...
  • 一.定時器 1. JS存在兩種定時器 setTimeout() 延遲定時器 setInterval() 迴圈定時器(“間隔器”) 定時器中的函數掛載在window對象,內部的this ——> window setTimerout(function(){ console.log('wuwei') }, ...
  • ‍ 寫在開頭 點贊 + 收藏 學會 近期產品期望在後臺發佈帖子或視頻時,需要添加 @用戶 的功能,以便用戶收到通知,例如“xxx在xxx提及了您!”。然而,現有的開源庫未能滿足我們的需求,例如 ant-design 的 Mentions 組件: 但是不難發現跟微信飛書對比下,有兩 ...
  • 前言 petite-vue 是為漸進增強而優化的另一種 Vue 發行版。它提供與標準 Vue 相同的模板語法和反應性心智模型。 不過,它專門針對在由伺服器框架呈現的現有 HTML 頁面上“散佈”少量交互進行了優化。 petite-vue,它在提供 vue 基本功能的同時,還能一個輕量級,簡單應用的微 ...
  • 概述了Nuxt3的六個關鍵生命周期鉤子用途:modules:before至build:before,指導如何在應用初始化、模塊管理、配置解析、模板處理及構建前執行自定義操作,附帶實例代碼,強化Nuxt應用的靈活性和可控性。 ...
  • 我們是袋鼠雲數棧 UED 團隊,致力於打造優秀的一站式數據中台產品。我們始終保持工匠精神,探索前端道路,為社區積累並傳播經驗價值。 本文作者:霽明 背景 我們產品中會有一些流程圖應用,例如審批中心的審批流程圖: 我們數棧產品內的流程圖,基本都是使用的 mxGraph 實現的,mxGraph 使用了S ...
  • Vue3發佈後,各家第三方庫開始陸續重構並支持 Vue3 ,國內兩大知名框架 Element Plus 和 Ant Design Vue 也相續發佈新版支持 Vue3。Element Plus 和 Ant Design Vue 都是基於 Vue.js 的 UI 組件庫,它們具備一系列可復用的組件和豐... ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...