-
Notifications
You must be signed in to change notification settings - Fork 634
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
前端进阶算法2:从Chrome V8源码看JavaScript数组(附赠腾讯面试题) #2
Comments
let stack = [1, 2, 3] |
快数组,慢数组,数组扩容和减容,学习了 |
对数组有了深刻的认识 |
请问数组直接定义为有十万个长度的数组,算快数组么,快速组的容量有什么限制么, |
慢数组把,hole太多就会是慢数组,毕竟不可能申请十万个空间全部放hole空着。。。 |
我们可以看到 JSArray 是继承自 JSObject 的,所以在 JavaScript 中,数组可以是一个特殊的对象,内部也是以 key-value 形式存储数据,所以 JavaScript 中的数组可以存放不同类型的值。 这里的因为所以关系不严谨吧? |
●快数组新容量是扩容后的容量 3 倍之多时 这句话没太明白,有大佬解释一波吗?感谢 |
动态扩容:数组初始length 空间是4,push第五个元素的时候 new_capacity = old_capacity /2 + old_capacity + 16 也就是22的空间出来 |
简介
数组、链表、栈、队列都是线性表,它表示的结构都是一段线性的结构,与之对应的就是非线性表,例如树、图、堆等,它表示的结构都非线性。
本节主要介绍 JavaScript 数组,在开始本章节前,思考一个问题:
我们知道在 JavaScript 中,可以在数组中保存不同类型值,并且数组可以动态增长,不像其它语言,例如 C,创建的时候要决定数组的大小,如果数组满了,就要重新申请内存空间。这是为什么喃?
本节从 Chrome v8 源码角度回答这个问题,分为四个方面:
FastElements
)下面进入正题吧!(文末有惊喜)😊
想要更多更快的学习本系列,可以关注公众号「前端瓶子君」和我的「Github(点击查看)」
一、数组(基础)
一种最基础的数据结构,每种编程语言都有,它编号从 0 开始,代表一组连续的储存结构,用来储存同一种类型的数据。
它的这种特定的存储结构(连续存储空间存储同一类型数据)决定了:
优点
缺点
查找: 根据下标随机访问的时间复杂度为 O(1);
插入或删除: 时间复杂度为 O(n);
在 JavaScript 中的数组几乎是万能的,它不光可以作为一个普通的数组使用,可以作为栈或队列使用。
数组:
栈:
队列:
二、JavaScript 中,数组可以保存不同类型值
看一下 Chrome v8 源码:
我们可以看到
JSArray
是继承自JSObject
的,所以在 JavaScript 中,数组可以是一个特殊的对象,内部也是以 key-value 形式存储数据,所以 JavaScript 中的数组可以存放不同类型的值。三、JavaScript 中,数组的存储
JSArray
继承于JSObject
,从注释上看,它有两种存储方式:FixedArray
,并且数组长度<= elements.length()
,push
或pop
时可能会伴随着动态扩容或减容HashTable
(哈希表),并且数组下标作为key
fast
模式下数组在源码里面叫FastElements
,而slow
模式下的叫做SlowElements
。1. 快数组(FastElements)
FixedArray
是 V8 实现的一个类似于数组的类,它表示一段连续的内存,可以使用索引直接定位。新创建的空数组默认就是快数组。当数组满(数组的长度达到数组在内存中申请的内存容量最大值)的时候,继续push
时,JSArray
会进行动态的扩容,以存储更多的元素。2. 慢数组(SlowElements)
慢数组以哈希表的形式存储在内存空间里,它不需要开辟连续的存储空间,但需要额外维护一个哈希表,与快数组相比,性能相对较差。
从源码中可以看出,它的内部就是一个 HashTable。
3. 什么时候会从 fast 转变为 slow 喃?
从 Chrome V8 源码上看,
所以,当处于以下情况时,快数组会被转变为慢数组:
例如:向快数组里增加一个大索引同类型值
当往
arr
增加一个2000
的索引时,arr
被转成慢数组。节省了大量的内存空间(从索引为 2 到索引为 2000)。4. 什么时候会从 slow 转变为 fast 喃?
我们已经知道在什么时候会出现由快变慢,那由慢变快就很简单了
当慢数组的元素可存放在快数组中且长度在 smi 之间且仅节省了50%的空间,则会转变为快数组
四、JavaScript 中,数组的动态扩容与减容(FastElements)
默认空数组初始化大小为
4
:在 JavaScript 中,当数组执行
push
操作时,一旦发现数组内存不足,将进行扩容。在 Chrome 源码中,
push
的操作是用汇编实现的,在 c++ 里嵌入的汇编,以提高执行效率,并且在汇编的基础上用 c++ 封装了一层,在编译执行的时候,会将这些 c++ 代码转成汇编代码。计算新容量的函数:
所以扩容后新容量计公式为:
即老的容量的 1.5 倍加上 16 。初始化为 4 个,当
push
第 5 个的时候,容量将会变成:接着申请一块这么大的内存,把老的数据拷过去,把新元素放在当前 length 位置,然后将数组的 length + 1,并返回 length。
所以,扩容可以分为以下几步:
push
操作时,发现数组内存不足整个过程,用户是无感知的,不像 C,需用用户手动申请内存空间。
当数组执行
pop
操作时,会判断pop
后数组的容量,是否需要进行减容。不同于数组的
push
使用汇编实现的,pop
使用 c++ 实现的。判断是否进行减容:
所以,当数组
pop
后,如果数组容量大于等于 length 的 2 倍,则进行容量调整,使用RightTrimFixedArray
函数,计算出需要释放的空间大小,做好标记,等待 GC 回收;如果数组容量小于 length 的 2 倍,则用 holes 对象填充。所以,减容可以分为以下几步:
pop
操作时,获取数组length
length - 1
上的元素(要删除的元素)length - 1
RightTrimFixedArray
函数,计算出需要释放的空间大小,并做好标记,等待GC
回收holes
对象填充五、解答开篇问题
JavaScript 中,
JSArray
继承自JSObject
,或者说它就是一个特殊的对象,内部是以 key-value 形式存储数据,所以 JavaScript 中的数组可以存放不同类型的值。它有两种存储方式,快数组与慢数组,初始化空数组时,使用快数组,快数组使用连续的内存空间,当数组长度达到最大时,JSArray
会进行动态的扩容,以存储更多的元素,相对慢数组,性能要好得多。当数组中hole
太多时,会转变成慢数组,即以哈希表的方式( key-value 的形式)存储数据,以节省内存空间。六、最后附赠一道前端面试题(腾讯):数组扁平化、去重、排序
关于
Array
的属性、方法这里不再做介绍,详看 MDN Array 。面试题:
答案:
关于 Set 请查阅 Set、WeakSet、Map及WeakMap
参考链接:
探究JS V8引擎下的“数组”底层实现
从Chrome源码看JS Array的实现
七、认识更多的前端道友,一起进阶前端开发
前端算法集训营第一期免费开营啦🎉🎉🎉,免费哟!
在这里,你可以和志同道合的前端朋友们(600+)一起进阶前端算法,从0到1构建完整的数据结构与算法体系。
在这里,瓶子君不仅介绍算法,还将算法与前端各个领域进行结合,包括浏览器、HTTP、V8、React、Vue源码等。
在这里,你可以每天学习一道大厂算法题(阿里、腾讯、百度、字节等等)或 leetcode,瓶子君都会在第二天解答哟!
更多福利等你解锁🔓🔓🔓!
在公众号「前端瓶子君」内回复「算法」即可加入。你的关注就是对瓶子君最大的支持😄😄😄
The text was updated successfully, but these errors were encountered: