Frontend Development 8 min read

Why Do Chrome and Firefox Sort Arrays Differently? Uncovering Their Algorithms

Although both browsers receive identical data, Chrome and Firefox display sorted results differently due to their distinct underlying sorting algorithms—Firefox uses merge sort while Chrome employs a hybrid of insertion and quick sort—this article explains the issue, demonstrates implementations, and compares performance and optimization techniques.

MaoDou Frontend Team
MaoDou Frontend Team
MaoDou Frontend Team
Why Do Chrome and Firefox Sort Arrays Differently? Uncovering Their Algorithms

Problem Description

Both Chrome and Firefox receive the same data from the API. After sorting, the same page shows different results in the two browsers.

Problem Investigation

Initially suspected front‑end sorting logic or other factors. After reviewing, the misuse of

sort()

was discovered.

sort()

without a comparator sorts elements as strings (lexicographically). Providing a comparator function allows custom ordering. The comparator must accept two arguments

a

and

b

and return a negative number if

a

should precede

b

, zero if equal, or a positive number otherwise.

Even after fixing the comparator, debugging showed that each iteration of the sorting process produced different intermediate results in the two browsers, although the final sorted array was the same. The discrepancy originates from the browsers’ internal sorting algorithms: Firefox uses merge sort, while Chrome uses a hybrid of insertion sort and quick sort (arrays with fewer than 10 elements use insertion sort, larger ones use quick sort).

Algorithm Implementations and Comparison

Merge Sort

Idea: recursively split the array into halves, sort each half, then merge the sorted halves.

<code>const merge = (left, right) => {
    const result = [];
    while (left.length && right.length) {
        if (left[0] <= right[0]) {
            result.push(left.shift());
        } else {
            result.push(right.shift());
        }
    }
    while (left.length) result.push(left.shift());
    while (right.length) result.push(right.shift());
    return result;
};
const mergeSort = arr => {
    const len = arr.length;
    if (len < 2) return arr;
    const middle = Math.floor(len / 2);
    const left = arr.slice(0, middle);
    const right = arr.slice(middle);
    return merge(mergeSort(left), mergeSort(right));
};
</code>

Quick Sort

Idea: choose a pivot, partition the array into elements smaller and larger than the pivot, then recursively sort the sub‑arrays.

<code>const quickSort1 = arr => {
    if (arr.length <= 1) return arr;
    const midIndex = Math.floor(arr.length / 2);
    const valArr = arr.splice(midIndex, 1);
    const midIndexVal = valArr[0];
    const left = [];
    const right = [];
    for (let i = 0; i < arr.length; i++) {
        if (arr[i] < midIndexVal) {
            left.push(arr[i]);
        } else {
            right.push(arr[i]);
        }
    }
    return quickSort1(left).concat(midIndexVal, quickSort1(right));
};
</code>

Insertion Sort

Suitable when most elements are already sorted. Idea: build a sorted sub‑array by inserting each unsorted element into its correct position.

<code>const insertionSort = (nums) => {
    for (let i = 1; i < nums.length; ++i) {
        let preIndex = i - 1;
        const temp = nums[i];
        while (preIndex >= 0 && nums[preIndex] > temp) {
            nums[preIndex + 1] = nums[preIndex];
            preIndex--;
        }
        nums[preIndex + 1] = temp;
    }
    return nums;
};
</code>

Performance Analysis

Time complexity:

Merge Sort – O(n log n)

Quick Sort – O(n log n)

Insertion Sort – O(n²)

Stability:

Merge Sort – stable

Quick Sort – unstable

Insertion Sort – stable

Optimization suggestions:

Insertion Sort – use binary search to find the insertion point (binary insertion).

<code>const binarySearch = (arr, maxIndex, value) => {
    let min = 0;
    let max = maxIndex;
    while (min <= max) {
        const mid = Math.floor((min + max) / 2);
        if (arr[mid] <= value) {
            min = mid + 1;
        } else {
            max = mid - 1;
        }
    }
    return min;
};
const insertionSort2 = (arr) => {
    for (let i = 1, len = arr.length; i < len; i++) {
        const temp = arr[i];
        const insertIndex = binarySearch(arr, i - 1, arr[i]);
        for (let preIndex = i - 1; preIndex >= insertIndex; preIndex--) {
            arr[preIndex + 1] = arr[preIndex];
        }
        arr[insertIndex] = temp;
    }
    return arr;
};
</code>

Further optimizations for merge sort include using

splice

instead of

slice

to reduce memory usage and switching to insertion sort for small sub‑arrays. Quick sort can be implemented in‑place, and a typical partition function is shown below.

<code>const swap = (arr, i, j) => {
    let temp = arr[i];
    arr[i] = arr[j];
    arr[j] = temp;
};
const partition = (arr, left, right) => {
    let pivot = left;
    let index = pivot + 1;
    for (let i = index; i <= right; i++) {
        if (arr[i] < arr[pivot]) {
            swap(arr, i, index);
            index++;
        }
    }
    swap(arr, pivot, index - 1);
    return index - 1;
};
const quickSort = (arr, left, right) => {
    const len = arr.length;
    left = typeof left !== 'number' ? 0 : left;
    right = typeof right !== 'number' ? len - 1 : right;
    if (left < right) {
        const index = partition(arr, left, right);
        quickSort(arr, left, index - 1);
        quickSort(arr, index + 1, right);
    }
    return arr;
};
</code>
frontendperformanceJavaScriptbrowser compatibilitysorting algorithms
MaoDou Frontend Team
Written by

MaoDou Frontend Team

Open-source, innovative, collaborative, win‑win – sharing frontend tech and shaping its future.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.