本篇文章给大家带来的内容是关于Immutable.js源码之List 类型的详细解析(附示例),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。

一、存储图解

我以下面这段代码为例子,画出这个List的存储结构:let myList = [];

for(let i=0;i<1100;i++) {

myList[i] = i;

}

debugger;//可以在这里打个断点调试

let immutableList = Immutable.List(myList)

debugger;

console.log(immutableList.set(1000, 'Remm'));

debugger;

console.log(immutableList.get(1000));

二、vector trie 的构建过程

我们用上面的代码为例子一步一步的解析。首先是把原生的list转换为inmutable的list 类型:export class List extends IndexedCollection {

// @pragma Construction

constructor(value) { // 此时的value就是上面的myList数组

const empty = emptyList();

if (value === null || value === undefined) {//判断是否为空

return empty;

}

if (isList(value)) {//判断是否已经是imutable的list类型

return value;

}

const iter = IndexedCollection(value);//序列化数组

const size = iter.size;

if (size === 0) {

return empty;

}

assertNotInfinite(size);

if (size > 0 && size < SIZE) { // 判断size是否超过32

return makeList(0, size, SHIFT, null, new VNode(iter.toArray()));

}

return empty.withMutations(list => {

list.setSize(size);

iter.forEach((v, i) => list.set(i, v));

});

}

。。。。。。

}

首先会创建一个空的listlet EMPTY_LIST;

export function emptyList() {

return EMPTY_LIST || (EMPTY_LIST = makeList(0, 0, SHIFT));

}

SHIFT的值为5,export const SHIFT = 5; // Resulted in best performance after ______?

再继续看makeList,可以清晰看到 List 的主要部分:function makeList(origin, capacity, level, root, tail, ownerID, hash) {

const list = Object.create(ListPrototype);

list.size = capacity - origin;// 数组的长度

list._origin = origin;// 数组的起始位置 一般是0

list._capacity = capacity;// 数组容量 等于 size

list._level = level;//树的深度,为0时是叶子结点。默认值是5,存储指数部分,用于方便位运算,增加一个深度,level值+5

list._root = root;// trie树实现

list._tail = tail;// 32个为一组,存放最后剩余的数据 其实就是 %32

list.__ownerID = ownerID;

list.__hash = hash;

list.__altered = false;

return list;

}

将传入的数据序列化// ArraySeq

iter = {

size: 数组的length,

_array: 传入数组的引用

}

判断size是否超过32,size > 0 && size < SIZE 这里 SIZE : export const SIZE = 1 << SHIFT;即 32。若没有超过32,所有数据都放在_tail中。

_root 和 _tail 里面的数据又有以下结构:// @VNode class

constructor(array, ownerID) {

this.array = array;

this.ownerID = ownerID;

}

可以这样调试查看:let myList = [];

for(let i=0;i<30;i++) {

myList[i] = i;

}

debugger;//可以在这里打个断点调试

console.log(Immutable.List(myList));

size如果超过32return empty.withMutations(list => {

list.setSize(size);//构建树的结构 主要是计算出树的深度

iter.forEach((v, i) => list.set(i, v));//填充好数据

});export function withMutations(fn) {

const mutable = this.asMutable();

fn(mutable);

return mutable.wasAltered() ? mutable.__ensureOwner(this.__ownerID) : this;

}

list.setSize(size)中有一个重要的方法setListBounds,下面我们主要看这个方法如何构建这颗树

这个方法最主要的作用是 确定 list的levelfunction setListBounds(list, begin, end) {

......

const newTailOffset = getTailOffset(newCapacity);

// New size might need creating a higher root.

// 是否需要增加数的深度 把 1 左移 newLevel + SHIFT 位 相当于 1 * 2 ^ (newLevel + SHIFT)

// 以 size为 1100 为例子 newTailOffset的值为1088 第一次 1088 > 2 ^ 10 树增加一层深度

// 第二次 1088 < 2 ^ 15 跳出循环 newLevel = 10

while (newTailOffset >= 1 << (newLevel + SHIFT)) {

newRoot = new VNode(

newRoot && newRoot.array.length ? [newRoot] : [],

owner

);

newLevel += SHIFT;

}

......

}function getTailOffset(size) {

// (1100 - 1) / 2^5 % 2^5 = 1088

return size < SIZE ? 0 : (((size - 1) >>> SHIFT) << SHIFT);

}

经过 list.setSize(size);构建好的结构

三、set 方法

listiter.forEach((v, i) => list.set(i, v));这里是将iter中的_array填充到

这里主要还是看看set方法如何设置数据set(index, value) {

return updateList(this, index, value);

}function updateList(list, index, value) {

......

if (index >= getTailOffset(list._capacity)) {

newTail = updateVNode(newTail, list.__ownerID, 0, index, value, didAlter);

} else {

newRoot = updateVNode(

newRoot,

list.__ownerID,

list._level,

index,

value,

didAlter

);

}

......

}function updateVNode(node, ownerID, level, index, value, didAlter) {

// 根据 index 和 level 计算 数据set的位置在哪

const idx = (index >>> level) & MASK;

// 利用递归 一步一步的寻找位置 直到找到最终的位置

if (level > 0) {

const lowerNode = node && node.array[idx];

const newLowerNode = updateVNode(

lowerNode,

ownerID,

level - SHIFT,

index,

value,

didAlter

);

......

// 把node节点的array复制一份生成一个新的节点newNode editableVNode函数见下面源码

newNode = editableVNode(node, ownerID);

// 回溯阶段将 子节点的引用赋值给自己

newNode.array[idx] = newLowerNode;

return newNode;

}

......

newNode = editableVNode(node, ownerID);

// 当递归到叶子节点 也就是level <= 0 将值放到这个位置

newNode.array[idx] = value;

......

return newNode;

}function editableVNode(node, ownerID) {

if (ownerID && node && ownerID === node.ownerID) {

return node;

}

return new VNode(node ? node.array.slice() : [], ownerID);

}

下面我们看看运行了一次set(0,0)的结果

整个结构构建完之后

下面我们接着看刚刚我们构建的list set(1000, 'Remm'),其实所有的set的源码上面已经解析过了,我们再来温习一下。

调用上面的set方法,index=1000,value='Remm'。调用updateList,继而调用updateVNode。通过const idx = (index >>> level) & MASK;计算要寻找的节点的位置(在这个例子中,idx的值依次是0->31->8)。 不断的递归查找,当 level <= 0 到达递归的终止条件,其实就是达到树的叶子节点,此时通过newNode = editableVNode(node, ownerID);创建一个新的节点,然后 newNode.array[8] = 'Remm'。接着就是开始回溯,在回溯阶段,自己把自己克隆一个,newNode = editableVNode(node, ownerID);,注意这里克隆的只是引用,所以不是深拷贝。然后再将idx位置的更新了的子节点重新赋值,newNode.array[idx] = newLowerNode;,这样沿着路径一直返回,更新路径上的每个节点,最后得到一个新的根节点。

更新后的list:

四、get 方法

了解完上面的list构建和set,我们再来看 immutableList.get(1000) 源码就是小菜一碟了。get(index, notSetValue) {

index = wrapIndex(this, index);

if (index >= 0 && index < this.size) {

index += this._origin;

const node = listNodeFor(this, index);

return node && node.array[index & MASK];

}

return notSetValue;

}function listNodeFor(list, rawIndex) {

if (rawIndex >= getTailOffset(list._capacity)) {

return list._tail;

}

if (rawIndex < 1 << (list._level + SHIFT)) {

let node = list._root;

let level = list._level;

while (node && level > 0) {

// 循环查找节点所在位置

node = node.array[(rawIndex >>> level) & MASK];

level -= SHIFT;

}

return node;

}

}

五、tire 树 的优点

来一张从网上盗来的图:

这种树的数据结构(tire 树),保证其拷贝引用的次数降到了最低,就是通过极端的方式,大大降低拷贝数量,一个拥有100万条属性的对象,浅拷贝需要赋值 99.9999万次,而在 tire 树中,根据其访问的深度,只有一个层级只需要拷贝 31 次,这个数字不随着对象属性的增加而增大。而随着层级的深入,会线性增加拷贝数量,但由于对象访问深度不会特别高,10 层已经几乎见不到了,因此最多拷贝300次,速度还是非常快的。

我上面所解析的情况有 构建、修改、查询。其实还有 添加 和 删除。

php tire树,Immutable.js源码之List 类型的详细解析(附示例)相关推荐

  1. 从template到DOM(Vue.js源码角度看内部运行机制)

    写在前面 这篇文章算是对最近写的一系列Vue.js源码的文章(github.com/answershuto-)的总结吧,在阅读源码的过程中也确实受益匪浅,希望自己的这些产出也会对同样想要学习Vue.j ...

  2. 补环境:vm2 transformer.js 源码分析

    在补环境框架的文件夹里执行 vm2 文件能成功得到结果,但是将合并了环境和原 js 文件后的代码内容单独提取出来通过 vm2 调用却报错提示 SyntaxError: Use of internal ...

  3. Vue.js 源码目录设计

    Vue.js 源码目录设计 Vue.js 的源码都在 src 目录下,其目录结构如下. src ├── compiler # 编译相关 ├── core # 核心代码 ├── platforms # ...

  4. MVVM架构~knockoutjs系列之从Knockout.Validation.js源码中学习它的用法

    说在前 有时,我们在使用一个插件时,在网上即找不到它的相关API,这时,我们会很抓狂的,与其抓狂,还不如踏下心来,分析一下它的源码,事实上,对于JS这种开发语言来说,它开发的插件的使用方法都在它的源码 ...

  5. vue.js源码学习分享(一)

    今天看了vue.js源码  发现非常不错,想一边看一遍写博客和大家分享 /*** Convert a value to a string that is actually rendered. *转换一 ...

  6. js怎么调用wasm_Long.js源码解析

    基于现在市面上到处都是 Vue/React 之类的源码分析文章实在是太多了.(虽然我也写过 Vite的源码解析 所以这次来写点不一样的.由于微信这边用的是 protobuf 来进行 rpc 调用.所以 ...

  7. underscore.js源码研究(5)

    概述 很早就想研究underscore源码了,虽然underscore.js这个库有些过时了,但是我还是想学习一下库的架构,函数式编程以及常用方法的编写这些方面的内容,又恰好没什么其它要研究的了,所以 ...

  8. 常用JS库源码 - store.js源码/underscore.js源码

    常用JS库源码 Store.js源码 "use strict" // Module export pattern from // https://github.com/umdjs/ ...

  9. 【Vue.js源码解析 一】-- 响应式原理

    前言 笔记来源:拉勾教育 大前端高薪训练营 阅读建议:建议通过左侧导航栏进行阅读 课程目标 Vue.js 的静态成员和实例成员初始化过程 首次渲染的过程 数据响应式原理 – 最核心的特性之一 准备工作 ...

最新文章

  1. Clang:LLVM的C语言家族前端
  2. CV02-FCN笔记
  3. ARP探测目标工具arping常用命令集合大学霸IT达人
  4. 代替Excel上载的方法
  5. mysql5.7 事件_MySQL 5.7新特性
  6. PAT乙级 1032 挖掘机技术哪家强 (20 分)
  7. 【2】测试用例设计方法-场景法
  8. 2019杭州云栖大会探营:神龙的秘密
  9. 狂神说Java--Java学习笔记(合集)
  10. 新浪新闻动态网页爬取+热点词云分析
  11. Spring的回炉重造
  12. 新晋小王子 doodoo.js Node.js开发框架
  13. 渲染字幕libass
  14. Microsoft Store无法显示错误,真正解决!
  15. AD6.9原理图打印去除背景色
  16. Flash打造星火飘落效果
  17. DEEPIN系统下安装wine
  18. Redis详解-更新中
  19. Leaflet自定义一个Control(L.Control)
  20. GIS开发常用的开源地图数据库介绍

热门文章

  1. Spring获取JavaBean的xml形式和注解形式
  2. 解决Vue3创建项目后的Error: Cannot find module ‘vue-loader-v16/package.json‘问题
  3. java sonar教程_SonarQube配置与使用教程.PDF
  4. 手机有一个时钟的标志_STM32F7系统配置控制器(SYSCFG)及复位和时钟控制(RCC)...
  5. BugkuCTF-MISC题1和0的故事
  6. java 数据队列_Java 数据结构 - 队列
  7. mysql集群需要几个ip_rac集群3组机器,scan到底需要几个IP?
  8. mysql乱码问题_mysql乱码问题
  9. linux 线程优先级算法,能讲一下在Linux系统中时间片是怎么分配的还有优先级的具体算法是...
  10. android下挂串口中断,请大神看看为啥串口中断无法打断定时器中断