前言
兄弟们周三好,最近在做年中总结的时候,发现自己更新文章的频率跟不上了,在社区中想要更多的文章曝光需要,更加频繁而且高效的输出优质的文章,而且整个互联网大环境依然不好,我们需要把精力更多的放在自我成长上面,而不是过多的无效加班上,所以需要我们更多地总结和学习新的内容,然后产出脑图或者MD文档。最近互联网大厂开启新一轮秋招,不知道身边的小伙伴有没有行动起来呢,anyway,我们需要更多地关注个人能力的成长上,这样才是合格的打工人,今天我们要来分享字节的真题,对一个事件循环的判断
在看了优弧大大的 科普社区玩法和建议的文章以后,我觉得很有必要提高自己文章的质量和输出更多自己总结归纳的内容。
概念
JavaScript的Array.prototype.sort()
方法是用来对数组的元素进行排序的。这个方法首先将数组的每个元素转换为字符串,然后基于字符的 UTF-16代码单元值 的序列进行排序。
这里提到的一个概念 UTF-16代码单元值
这个是什么呢?平常我们是经常提到 UTF-8
,这个估计很多人都熟悉,我们可以其实可以猜到其实就是类似的 Unicode的编码方式
UTF-16 是什么?
UTF-16(Unicode Transformation Format 16-bit)是一种 Unicode
编码方案,它使用16位代码单元来表示字符。UTF-16编码中的字符可以由一个或两个16位代码单元组成,取决于字符的编码范围。
大多数常见的字符(包括英文字母、数字和常见的符号)在UTF-16中使用一个16位代码单元来表示,其值与字符的 Unicode
码点相同。例如,拉丁字母 "A"的Unicode码点是U+0041,它在UTF-16中表示为16位代码单元0x0041。
但对于一些较大的字符,它们的Unicode码点在基本多文种平面(BMP)之外,因此需要使用两个16位代码单元来表示。这些字符的编码方式称为"代理对"(surrogate pairs)。代理对由一个高位(high surrogate)和一个低位(low surrogate)组成,它们的组合用来表示字符的Unicode码点。代理对的取值范围为:
- 高位(High Surrogate):0xD800 - 0xDBFF
- 低位(Low Surrogate):0xDC00 - 0xDFFF
通过组合高位和低位的代理对,可以表示从BMP之外的Unicode字符。例如,表情符号😀的Unicode码点是U+1F600,它在UTF-16中表示为代理对(0xD83D, 0xDE00)。
总之,UTF-16的代码单元值可以是16位无符号整数,范围从0x0000到0xFFFF,但对于代理对,它们的值在特定的范围内。不同于UTF-8,UTF-16中的字符长度是固定的(要么1个代码单元,要么2个代码单元),而UTF-8中的字符长度可变。
我们使用代码来解释其实是利用到关键的 API charCodeAt
js
// 定义一个字符串
var myString = "Hello, 你好!";
// 遍历字符串中的每个字符,并获取其UTF-16代码值
for (var i = 0; i < myString.length; i++) {
var char = myString.charAt(i);
var utf16Value = myString.charCodeAt(i);
// 输出字符和其UTF-16代码值
console.log("字符: " + char + ",UTF-16代码值: " + utf16Value);
}
Sort的传入参数
你也可以为sort()
方法提供一个可选的比较函数来自定义排序规则。
-
默认排序行为:
- 没有提供比较函数时,数组元素被转化为字符串,然后根据其字符的UTF-16代码单元值的序列进行排序。
- 这种默认行为可能会导致一些意外的结果,特别是在排序数字数组时。例如,数组
[10, 5, 40]
将被排序为[10, 40, 5]
,因为字符串形式的"10"在"40"之前。
-
使用比较函数:
- 您可以提供一个比较函数来告诉
sort()
如何排序数组元素。 - 这个函数应该接收两个参数,通常命名为
a
和b
。 - 如果
a
应该位于b
之前,则返回一个小于0的数值。 - 如果
a
和b
相等,则返回0。 - 如果
a
应该位于b
之后,则返回一个大于0的数值。
- 您可以提供一个比较函数来告诉
这部分很好理解,我们平常经常性地使用 传入比较函数,下面是例子
js
var myArr = [1,3,2,6,5,4]
var mySort = (arr) => {
console.log(arr.sort((a,b) => b - a)) // [6, 5, 4, 3, 2, 1]
console.log(arr.sort(a,b) => a - b)) // [1, 2, 3, 4, 5, 6]
}
mySort(myArr);
背后到底是怎么排序的呢?
至于背后具体使用的排序算法,这实际上取决于浏览器和它的JavaScript引擎。V8(Chrome的JavaScript引擎)在过去使用了多种排序算法,包括插入排序和快速排序的组合。
V8对小数组使用插入排序 ,因为在小数组上,插入排序可能比其他复杂的算法更快。对于更大的数组,V8 使用 快速排序。
但是,这只是 V8 的实现,并不是 ECMAScript 标准的规定。其他浏览器的 JavaScript 引擎可能会使用不同的排序算法。
下面我们简单举两个 插入排序 和 快速排序 的例子
插入排序
核心思想是 该算法将一个数组分成已排序和未排序两部分,然后逐个将未排序部分的元素插入已排序部分的合适位置,直到整个数组被排序完成。时间复杂度为O(n^2), 可以看出时间复杂度来说是不及快速排序的,对于那些大型数据集,更高效的排序算法如快速排序或归并排序通常更合适。
js
function insertionSort(arr) {
const n = arr.length;
for (let i = 1; i < n; i++) {
// 从第二个元素开始,将其插入已排序部分的合适位置
const currentElement = arr[i];
let j = i - 1;
// 在已排序部分中找到合适的位置,将当前元素插入
while (j >= 0 && arr[j] > currentElement) {
arr[j + 1] = arr[j]; // 向右移动元素
j--;
}
arr[j + 1] = currentElement; // 插入当前元素到正确的位置
}
return arr;
}
// 示例用法
const unsortedArray = [3, 6, 8, 10, 1, 2, 1];
const sortedArray = insertionSort(unsortedArray);
console.log(sortedArray); // 输出 [1, 1, 2, 3, 6, 8, 10]
快速排序
核心思想是分治的策略实现的 时间复杂度是 O(n log n)
js
function quickSort(arr) {
if (arr.length <= 1) {
return arr; // 如果数组只有一个元素或为空,无需排序,直接返回
}
// 选择一个基准元素,通常选择第一个或最后一个元素
const pivot = arr[0];
// 创建两个数组,用于存放比基准元素小和大的元素
const less = [];
const greater = [];
// 从第二个元素开始遍历数组,将元素分别放入less和greater数组中
for (let i = 1; i < arr.length; i++) {
if (arr[i] <= pivot) {
less.push(arr[i]);
} else {
greater.push(arr[i]);
}
}
// 递归地对less和greater数组进行快速排序,然后将它们与基准元素合并
return quickSort(less).concat(pivot, quickSort(greater));
}
// 示例用法
const unsortedArray = [3, 6, 8, 10, 1, 2, 1];
const sortedArray = quickSort(unsortedArray);
console.log(sortedArray); // 输出 [1, 1, 2, 3, 6, 8, 10]
小结
总之,如果你需要确切的排序行为或特定的性能特性,最好自己实现排序算法或使用经过良好测试的第三方库。
这篇文章到这里就结束了,水平有限难免有纰漏,欢迎纠错。最后希望帮忙点点赞,这对我创作是无比的肯定和动力。希望可以帮到你