为了账号安全,请及时绑定邮箱和手机立即绑定

Immutable 源码解析 - List 对象

标签:
React

什么是 Trie

  • 查询每个条目的复杂度跟树中的元素个数无关
  • 查询复杂度只跟深度有关 O(deep)
  • 修改某个节点可以轻松记录查找的路径

Immutable.List 的 Trie

  • Immutable.List 的 Trie 结构是一个 32 叉宽的结构

List 构建

// 示例代码创建一个 1100 长度的 list
const Immutable = require('immutable')
let arr = [];
for (let i = 0; i < 1100; i++) {
  arr[i] = i;
}
const list = Immutable.List(arr)
debugger;
console.log(list)

创建空的 List 对象


只有在创建空 List 时才调用 makeList这时的 list 才会有 ownerID 属性, lilst.ownerID 用于标识需要进行 VNode 对象引用比较VNode 的 ownerID 用于判断引用 === 在初始化空 list 时防止多次创建list 创建完毕后会删除 list.ownerID再次更新 list 时就必须返回新的 VNode 变为可变对象

// 生成一个空的 List 对象
  var EMPTY_LIST;
  function emptyList() {
    return EMPTY_LIST || (EMPTY_LIST = makeList(0, 0, SHIFT));
  }

  function makeList(origin, capacity, level, root, tail, ownerID, hash) {
    var list = Object.create(ListPrototype);
    list.size = capacity - origin;
    list._origin = origin;
    list._capacity = capacity;
    list._level = level;
    list._root = root;
    list._tail = tail;
    list.__ownerID = ownerID; // 只在空 list 初始化时存在
    list.__hash = hash;
    list.__altered = false;
    return list;
  }

设置 List 的边界

根据传入数组的长度来决定如何构建树的深度和结构

setListBounds 根据 List 的 size 设置容量、_root、_tail 的信息

描述 trie 树尺寸的常量

  // Constants describing the size of trie nodes.
  var SHIFT = 5; 
  var SIZE = 1 << SHIFT; // 32
  var MASK = SIZE - 1; // 31

创建 list 结构

function setListBounds(list, begin, end) {
  // ...

  var newCapacity = 
  var newLevel = list._level; // 初始 1 层结构是 5
  var newRoot = list._root;

  var oldTailOffset = getTailOffset(oldCapacity);
  var newTailOffset = getTailOffset(newCapacity); // 根据容量计算 _tail 的位置offset 为 32 的倍数

  // New size might need creating a higher root. SHIFT = 5, 这里 1 <<< (5 + 5) = 1024, 也是 32 * 32 的倍数此时一层最大就只有 1024 个加一层结构
  while (newTailOffset >= 1 << (newLevel + SHIFT)) { // newTailOffset >= (1 << (newLevel + SHIFT))
    newRoot = new VNode(newRoot && newRoot.array.length ? [newRoot] : [], owner);
    newLevel += SHIFT; // 加一层结构
  }

    // Locate or create the new tail. // 创建 _tail
    var oldTail = list._tail;
    var newTail = newTailOffset < oldTailOffset ?
      listNodeFor(list, newCapacity - 1) :
      newTailOffset > oldTailOffset ? new VNode([], owner) : oldTail;

    // ...

    // 初始化 list 结构
    if (list.__ownerID) {
      list.size = newCapacity - newOrigin;
      list._origin = newOrigin;
      list._capacity = newCapacity;
      list._level = newLevel;
      list._root = newRoot;
      list._tail = newTail;
      list.__hash = undefined;
      list.__altered = true;
      return list;
    }
}

最后得到的 list 结构

getTailOffset 找到 tail 的起始位置

如果 size 小于 32 就是从 0 开始size - 1 向左移 5 位再向右移 5 位会得到一个小于 size 中倍数 32 的最大数。size = 1100, 得出 1088, 得出的数就是 tail 开始的位置

  var SHIFT = 5; 
  var SIZE = 1 << SHIFT; // 32

  function getTailOffset(size) {
    return size < SIZE ? 0 : (((size - 1) >>> SHIFT) << SHIFT);
  }

构建 _root根据 size 决定 trie 的深度1 << (newLevel + SHIFT)第一次 1 << 10 = 1024 也就是 32 * 32 一层的数量size 超过一层的空间 SHIFT + 5增加一层深度

    // 初始 SHIFT = 5, 这里 1 <<< (5 + 5) = 1024, 也是 32 * 32 的倍数此时一层最大就只有 1024 个加一层结构
    while (newTailOffset >= 1 << (newLevel + SHIFT)) { // newTailOffset >= (1 << (newLevel + SHIFT))
      newRoot = new VNode(newRoot && newRoot.array.length ? [newRoot] : [], owner);
      newLevel += SHIFT; // 加一层q结构
    }

List.set

withMutations

List.set 在 withMutations 回调中可以暂时使用可变对象的操作方式执行 updateList 方法进行赋值

withMutaions 在回调给 fn 传了一个可以进行可变操作的 list操作完后返回新对象

export function withMutations(fn) {
  const mutable = this.asMutable();
  fn(mutable);
  return mutable.wasAltered() ? mutable.__ensureOwner(this.__ownerID) : this;
}

updateList 更新 list

  function updateList(list, index, value) {
    index = wrapIndex(list, index); // 确定好 index 

    if (index !== index) {
      return list;
    }

    if (index >= list.size || index < 0) {
      return list.withMutations(function(list ) {
        index < 0 ?
          setListBounds(list, index).set(0, value) : // 小于 0 头部加一个
          setListBounds(list, 0, index + 1).set(index, value) 
      });
    }

    index += list._origin; // 找到在 list 上的位置

    var newTail = list._tail;
    var newRoot = list._root;
    var didAlter = MakeRef(DID_ALTER);
    // 更新的 index, 在 _tail 还是 _root 中, 分别更新
    if (index >= getTailOffset(list._capacity)) { // index 在 _tail 中 更新 _tail
      newTail = updateVNode(newTail, list.__ownerID, 0, index, value, didAlter); // 在范围外的的用 tail 结构
    } else { // index 在 _root 中
      newRoot = updateVNode(newRoot, list.__ownerID, list._level, index, value, didAlter); // 在范围内的 root 结构
    }

    if (!didAlter.value) {
      return list;
    }
    // 初始化的时候有 ownerID 防止多次创建创建完了后就设置为 undefined
    if (list.__ownerID) {
      list._root = newRoot;
      list._tail = newTail;
      list.__hash = undefined;
      list.__altered = true;
      return list;
    }
    return makeList(list._origin, list._capacity, list._level, newRoot, newTail);
  }

这里看下 List 结构的更新root 为 trie 结构上更新时传入树深度的 level而更新 tail 这种扁平化数组结构不需要深度 level

    // 更新的 index, 在 _tail 还是 _root 中, 分别更新
    if (index >= getTailOffset(list._capacity)) { // index 在 _tail 中 更新 _tail
      newTail = updateVNode(newTail, list.__ownerID, 0, index, value, didAlter); // 在范围外的的用 tail 结构
    } else { // index 在 _root 中
      newRoot = updateVNode(newRoot, list.__ownerID, list._level, index, value, didAlter); // 在范围内的 root 结构
    }

updateNode

updateVNode 分两段逻辑一段是更新 root 的 trie 树一段是更新扁平化 VNode 数组逻辑比如 tail 结构或者是 root trie 树的最后一层。

updateNode 更新扁平化结构

通过换位找到 index 在当前层级数组上的 idx 后 tail 这种扁平化的 VNode 会通过 editableVNode 拷贝一个新的 newNode 用来更新返回的也是这个 newNode 新的 VNode替换掉原来的 tail 或者 root 的最下层 VNode

updateNode 更新 trie 树结构

通过换位运算来找到 index 在当前层级下的 idx 索引, 当 level > 0 时表示此时的 VNode 不是最下层通过递归 updateVNode 找到最下一层进行操作

editableVNode

换位运算

// 十进制 index = 1000, level = 10, MASK = 31
// 二进制 MASK 011111
var idx = (index >>> level) & MASK

第一轮位运算, 得到 index 的位置在 trie 上第一层 VNode.array 的序号

// 十进制 1000, 二进制 11111 01000 向右移 10 位得到 0, 再 & MASK 还是 0 
// 十进制 1088, 二进制 1 00010 00000 向右移 10 位得到 1, 再 & MASK 是 1

第二轮位运算level - 5 = 5 得到 index 在下一层 VNode.array 的序号

// 递归 level - 5 = 5
// 十进制 1000, 二进制 11111 01000 向右移 10 位得到 11111, 再 & MASK 还是 7
// 十进制 1088, 二进制 1 00010 00000 向右移 10 位得到 100010 , 再 & MASK 是 10 

此时 level - 5 = 0 不会进入 if (level > 0) 的递归操作中代表已经找到了 root 中 trie 的最下一层index 的位置进入操作扁平化数组的逻辑中不断返回到上一层进行路径的回溯创建一直到 root 上返回新的 root。

List.get

根据 index 找到扁平化内容的 VNodeindex & MASK 结果是 0 - 31 得到序号从 VNode.array 中找到 value

listNode

先确认是不是在 tail 上在 root 上就 level - 5 进行循环找到最后一层 VNode

参考

点击查看更多内容
1人点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
Web前端工程师
手记
粉丝
4
获赞与收藏
25

关注作者,订阅最新文章

阅读免费教程

感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消