摘要:首先是把原生的轉(zhuǎn)換為的類(lèi)型此時(shí)的就是上面的數(shù)組判斷是否為空判斷是否已經(jīng)是的類(lèi)型序列化數(shù)組判斷是否超過(guò)。。。。。。若沒(méi)有超過(guò),所有數(shù)據(jù)都放在中。通過(guò)計(jì)算要尋找的節(jié)點(diǎn)的位置在這個(gè)例子中,的值依次是。我上面所解析的情況有構(gòu)建修改查詢(xún)。
一、存儲(chǔ)圖解
我以下面這段代碼為例子,畫(huà)出這個(gè)List的存儲(chǔ)結(jié)構(gòu):
let myList = []; for(let i=0;i<1100;i++) { myList[i] = i; } debugger;//可以在這里打個(gè)斷點(diǎn)調(diào)試 let immutableList = Immutable.List(myList) debugger; console.log(immutableList.set(1000, "Remm")); debugger; console.log(immutableList.get(1000));二、vector trie 的構(gòu)建過(guò)程
我們用上面的代碼為例子一步一步的解析。首先是把原生的list轉(zhuǎn)換為inmutable的list 類(lèi)型:
export class List extends IndexedCollection { // @pragma Construction constructor(value) { // 此時(shí)的value就是上面的myList數(shù)組 const empty = emptyList(); if (value === null || value === undefined) {//判斷是否為空 return empty; } if (isList(value)) {//判斷是否已經(jīng)是imutable的list類(lèi)型 return value; } const iter = IndexedCollection(value);//序列化數(shù)組 const size = iter.size; if (size === 0) { return empty; } assertNotInfinite(size); if (size > 0 && size < SIZE) { // 判斷size是否超過(guò)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)); }); } 。。。。。。 }
首先會(huì)創(chuàng)建一個(gè)空的list
let 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 ______?
再繼續(xù)看makeList,可以清晰看到 List 的主要部分:
function makeList(origin, capacity, level, root, tail, ownerID, hash) { const list = Object.create(ListPrototype); list.size = capacity - origin;// 數(shù)組的長(zhǎng)度 list._origin = origin;// 數(shù)組的起始位置 一般是0 list._capacity = capacity;// 數(shù)組容量 等于 size list._level = level;//樹(shù)的深度,為0時(shí)是葉子結(jié)點(diǎn)。默認(rèn)值是5,存儲(chǔ)指數(shù)部分,用于方便位運(yùn)算,增加一個(gè)深度,level值+5 list._root = root;// trie樹(shù)實(shí)現(xiàn) list._tail = tail;// 32個(gè)為一組,存放最后剩余的數(shù)據(jù) 其實(shí)就是 %32 list.__ownerID = ownerID; list.__hash = hash; list.__altered = false; return list; }
將傳入的數(shù)據(jù)序列化
// ArraySeq iter = { size: 數(shù)組的length, _array: 傳入數(shù)組的引用 }
判斷size是否超過(guò)32,size > 0 && size < SIZE 這里 SIZE : export const SIZE = 1 << SHIFT;
即 32。若沒(méi)有超過(guò)32,所有數(shù)據(jù)都放在_tail中。
_root 和 _tail 里面的數(shù)據(jù)又有以下結(jié)構(gòu):
// @VNode class constructor(array, ownerID) { this.array = array; this.ownerID = ownerID; }
可以這樣調(diào)試查看:
let myList = []; for(let i=0;i<30;i++) { myList[i] = i; } debugger;//可以在這里打個(gè)斷點(diǎn)調(diào)試 console.log(Immutable.List(myList));
size如果超過(guò)32
return empty.withMutations(list => { list.setSize(size);//構(gòu)建樹(shù)的結(jié)構(gòu) 主要是計(jì)算出樹(shù)的深度 iter.forEach((v, i) => list.set(i, v));//填充好數(shù)據(jù) });
export function withMutations(fn) { const mutable = this.asMutable(); fn(mutable); return mutable.wasAltered() ? mutable.__ensureOwner(this.__ownerID) : this; }
list.setSize(size)中有一個(gè)重要的方法setListBounds,下面我們主要看這個(gè)方法如何構(gòu)建這顆樹(shù)
這個(gè)方法最主要的作用是 確定 list的level
function setListBounds(list, begin, end) { ...... const newTailOffset = getTailOffset(newCapacity); // New size might need creating a higher root. // 是否需要增加數(shù)的深度 把 1 左移 newLevel + SHIFT 位 相當(dāng)于 1 * 2 ^ (newLevel + SHIFT) // 以 size為 1100 為例子 newTailOffset的值為1088 第一次 1088 > 2 ^ 10 樹(shù)增加一層深度 // 第二次 1088 < 2 ^ 15 跳出循環(huán) 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); }
經(jīng)過(guò) list.setSize(size);構(gòu)建好的結(jié)構(gòu)
三、set 方法iter.forEach((v, i) => list.set(i, v));這里是將iter中的_array填充到list
這里主要還是看看set方法如何設(shè)置數(shù)據(jù)
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) { // 根據(jù) index 和 level 計(jì)算 數(shù)據(jù)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節(jié)點(diǎn)的array復(fù)制一份生成一個(gè)新的節(jié)點(diǎn)newNode editableVNode函數(shù)見(jiàn)下面源碼 newNode = editableVNode(node, ownerID); // 回溯階段將 子節(jié)點(diǎn)的引用賦值給自己 newNode.array[idx] = newLowerNode; return newNode; } ...... newNode = editableVNode(node, ownerID); // 當(dāng)遞歸到葉子節(jié)點(diǎn) 也就是level <= 0 將值放到這個(gè)位置 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); }
下面我們看看運(yùn)行了一次set(0,0)的結(jié)果
整個(gè)結(jié)構(gòu)構(gòu)建完之后
下面我們接著看剛剛我們構(gòu)建的list set(1000, "Remm"),其實(shí)所有的set的源碼上面已經(jīng)解析過(guò)了,我們?cè)賮?lái)溫習(xí)一下。
調(diào)用上面的set方法,index=1000,value="Remm"。調(diào)用updateList,繼而調(diào)用updateVNode。通過(guò)const idx = (index >>> level) & MASK;計(jì)算要尋找的節(jié)點(diǎn)的位置(在這個(gè)例子中,idx的值依次是0->31->8)。 不斷的遞歸查找,當(dāng) level <= 0 到達(dá)遞歸的終止條件,其實(shí)就是達(dá)到樹(shù)的葉子節(jié)點(diǎn),此時(shí)通過(guò)newNode = editableVNode(node, ownerID);創(chuàng)建一個(gè)新的節(jié)點(diǎn),然后 newNode.array[8] = "Remm"。接著就是開(kāi)始回溯,在回溯階段,自己把自己克隆一個(gè),newNode = editableVNode(node, ownerID);,注意這里克隆的只是引用,所以不是深拷貝。然后再將idx位置的更新了的子節(jié)點(diǎn)重新賦值,newNode.array[idx] = newLowerNode;,這樣沿著路徑一直返回,更新路徑上的每個(gè)節(jié)點(diǎn),最后得到一個(gè)新的根節(jié)點(diǎn)。
更新后的list:
四、get 方法了解完上面的list構(gòu)建和set,我們?cè)賮?lái)看 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) { // 循環(huán)查找節(jié)點(diǎn)所在位置 node = node.array[(rawIndex >>> level) & MASK]; level -= SHIFT; } return node; } }五、tire 樹(shù) 的優(yōu)點(diǎn)
來(lái)一張從網(wǎng)上盜來(lái)的圖:
這種樹(shù)的數(shù)據(jù)結(jié)構(gòu)(tire 樹(shù)),保證其拷貝引用的次數(shù)降到了最低,就是通過(guò)極端的方式,大大降低拷貝數(shù)量,一個(gè)擁有100萬(wàn)條屬性的對(duì)象,淺拷貝需要賦值 99.9999萬(wàn)次,而在 tire 樹(shù)中,根據(jù)其訪(fǎng)問(wèn)的深度,只有一個(gè)層級(jí)只需要拷貝 31 次,這個(gè)數(shù)字不隨著對(duì)象屬性的增加而增大。而隨著層級(jí)的深入,會(huì)線(xiàn)性增加拷貝數(shù)量,但由于對(duì)象訪(fǎng)問(wèn)深度不會(huì)特別高,10 層已經(jīng)幾乎見(jiàn)不到了,因此最多拷貝300次,速度還是非??斓摹?/p>
我上面所解析的情況有 構(gòu)建、修改、查詢(xún)。其實(shí)還有 添加 和 刪除。
其實(shí)Immutable.js 部分參考了 Clojure 中的PersistentVector的實(shí)現(xiàn)方式。所以可以看看下面這篇文章:
https://hypirion.com/musings/...
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/99338.html
摘要:那對(duì)于結(jié)構(gòu)又要如何處理,沒(méi)有結(jié)構(gòu)的索引,那怎么辦呢我們把鍵名變?yōu)楣V稻涂梢岳病1硎敬嬖?,表示不存在。例如轉(zhuǎn)換為二進(jìn)制后為每位為一組,假定為取出需要的位。類(lèi)對(duì)應(yīng)帶的,代表其子元素的數(shù)量。 上一片文章介紹的是 List 結(jié)構(gòu)。那對(duì)于 Map 結(jié)構(gòu)又要如何處理,沒(méi)有 List 結(jié)構(gòu)的索引,那怎么辦呢? 我們把鍵名變?yōu)楣V稻涂梢岳瞺 HAMT:Hash Arrey Mapped Trie ...
摘要:文章博客地址所創(chuàng)建的數(shù)據(jù)有一個(gè)迷人的特性數(shù)據(jù)創(chuàng)建后不會(huì)被改變。是的基類(lèi),使用該類(lèi)時(shí)需要至少繼承其子類(lèi)中的一個(gè)。總結(jié)所提供的和固有的各有優(yōu)勢(shì),未來(lái)有可能制定一套原生的規(guī)范,在這之前,是一個(gè)不錯(cuò)的選擇。參考資料官方文檔 文章博客地址:http://pinggod.com/2016/Immutable/ Immutable.js 所創(chuàng)建的數(shù)據(jù)有一個(gè)迷人的特性:數(shù)據(jù)創(chuàng)建后不會(huì)被改變。我們使用 ...
摘要:持久化數(shù)據(jù)提供可修改的,這些不在原地更新數(shù)據(jù),而是產(chǎn)生新的更新后的數(shù)據(jù)。提供了很多持久化不可變數(shù)據(jù)結(jié)構(gòu),包括以及。也提供了惰性允許有效的方法鏈?zhǔn)讲僮鳎绾?,不用?chuàng)建中介變量。 簡(jiǎn)介 JavaScript中的不可變集合 不可變數(shù)據(jù)一旦創(chuàng)建就不能改變,這樣可簡(jiǎn)化應(yīng)用開(kāi)發(fā)、無(wú)防御復(fù)制、啟用更先進(jìn)的內(nèi)存方案,以及使用更簡(jiǎn)單的邏輯檢查更新。持久化數(shù)據(jù)提供可修改的API,這些API不在原地更新...
摘要:與持久化工程師花了年時(shí)間打造,與同期出現(xiàn)。有持久化數(shù)據(jù)結(jié)構(gòu),如等,并發(fā)安全??偨Y(jié)篇幅有限,時(shí)間也比較晚了,關(guān)于前端數(shù)據(jù)的扁平化與持久化處理先講這么多了,有興趣的同學(xué)可以關(guān)注下,后面有時(shí)間會(huì)多整理分享。 (PS: 時(shí)間就像海綿里的水,擠到?jīng)]法擠,只能擠擠睡眠時(shí)間了~ 知識(shí)點(diǎn)還是需要整理的,付出總會(huì)有收獲,tired but fulfilled~) 前言 最近業(yè)務(wù)開(kāi)發(fā),從零搭建網(wǎng)頁(yè)生成器,...
摘要:在中,的返回值是,即一個(gè)新的對(duì)象。該方法在中的和中均有使用,但是此處的方法返回的是一個(gè)布爾值。是排序方法,可以傳入一個(gè)函數(shù),通過(guò)返回值的正負(fù)確定元素的前后排序順序。 1.immutableObj在復(fù)制的時(shí)候,復(fù)制的是引用。 === 比較的是引用是否一樣。而is()和equal()表示的是值是否一樣,什么是值,我認(rèn)為就是將一個(gè)對(duì)象Json.stringify()之后的的數(shù)據(jù)。 總體而言,...
閱讀 3077·2021-11-18 10:07
閱讀 3854·2021-11-17 17:00
閱讀 2167·2021-11-15 18:01
閱讀 986·2021-10-11 10:58
閱讀 3513·2021-09-10 10:50
閱讀 3675·2021-08-13 15:05
閱讀 1279·2019-08-30 15:53
閱讀 2711·2019-08-29 13:01