国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

Immutable源碼解析與性能優(yōu)化

233jl / 2829人閱讀

摘要:修改的節(jié)點(diǎn)和該父級(jí)鏈路上都變成新的對(duì)象顯然是最優(yōu)方案。如果你對(duì)比的兩個(gè)中,一個(gè)被過,另一個(gè)數(shù)據(jù)又是由其衍生出來的,那效率將是最高的算法的原理與優(yōu)化檢測(cè)本地中是否存在已過當(dāng)前對(duì)象字符串。

Immutable原理解析 簡(jiǎn)介 what is Immutable

1.不可變,一成不變的

2.對(duì)immutable數(shù)據(jù)的每次修改操作都會(huì)返回一個(gè)新的data

掏出一副老生常談的圖

immutable的優(yōu)點(diǎn)

1.歷史回退(同時(shí)不浪費(fèi)內(nèi)存),時(shí)間旅行之類的easy!

2.函數(shù)式編程

3.降低代碼的復(fù)雜度

數(shù)據(jù)類型

List: 類Array

Map:類Object/Map

Set:類Set

OrderMap/Set:有序Map/Set

....還有些不常用的數(shù)據(jù)類型

API fromJS/toJS

對(duì)傳入對(duì)象或數(shù)組進(jìn)行deepImmutable,array轉(zhuǎn)成List,Object轉(zhuǎn)成Map

const a = Immutable.fromJS({a:1,b:2})

console.log(a)    //Map {size: 2, _root: ArrayMapNode, __ownerID: undefined, __hash: 1014196085, __altered: false}

//定制化fromJS,根據(jù)key索引和value決定你想將他淺immutable還是深immutable,或者轉(zhuǎn)換成其他immutable類型
const b = Immutable.fromJS({a:["a","b"],b:2},(key,value)=>{

    const isIndexed = Immutable.Iterable.isIndexed(value);
    return isIndexed ? value.toList() : value.toOrderedMap();

})

a.toJS() // {a:1,b:2}
Map/List Map

語法上同時(shí)兼容了ES6 Map,支持[key,value]形式傳入

const MapA = Immutable.Map([["a",1],["b","2"]])

const MapB = Immutable,Map({a:1})

console.log(MapA.toJS(),MapB.toJS()) // {a:1,b:2} {a:1}
  
List
const ListA = Immutable.List([["a",1],["b","2"]])

ListA.toJS() // [["a",1],["b","2"]]

  
size

獲取大小

const ListA = Immutable.List([["a",1],["b","2"]])

const MapA = Immutable.Map({a:{a:1}})

ListA.size // 2

MapA.size // 1

  
get/getIn

使用方式:get(key:any, notSetValue) / getIn(keyPath:array,notSeValue)

const obj = Immutable.fromJS({a:{a:8}})

console.log(obj.get("a"),obj.getIn(["a","a"])) //Map.... 8

console.log(obj.get("b","joker"),obj.getIn(["b","b","b"],"joker")) //joker joker

const array = Immutable.fromJS([{a:1},"2"])

array.get(0).toJS() // {a:1}

array.getIn([0,"a"]) // 1

從此優(yōu)雅寫代碼

以前的我們
if(a && a.data && a.data.productList && a.data.productList.length > 0) 

現(xiàn)在的我們
$immutable.getIn(["data","productList"],List()).size > 0

immutable除了對(duì)嵌套形式的數(shù)據(jù)進(jìn)行分離外,對(duì)于同一層級(jí)的數(shù)據(jù)也進(jìn)行了分割,見下文_tail+__root區(qū)間存儲(chǔ)

set/setIn
const ListA = Immutable.from({a:{a:1}})

const ListB = ListA.set("a",{o:77}) // {a:{o:77}}

ListB === ListA // false

ListA.setIn(["a","a"],"7777") // {a:{a:777}}

set/setIn是我們最常用的api,其內(nèi)部實(shí)現(xiàn)和update/updateIn一樣。也是immutable之所以immutable的核心所在

在文章剛開始提到的immutable原理圖中,為什么immutable在改變一個(gè)節(jié)點(diǎn)后,該父節(jié)點(diǎn)的鏈路上都變成了新的節(jié)點(diǎn),一方面和實(shí)際需要有關(guān),一方面也與set方法的實(shí)現(xiàn)有關(guān)。

從實(shí)際需要的角度,數(shù)據(jù)如果想immutable化,即前后完全是兩個(gè)對(duì)象,同時(shí)為了避免deepClone的性能問題,達(dá)到不變數(shù)據(jù)內(nèi)存的盡可能復(fù)用。修改的節(jié)點(diǎn)和該父級(jí)鏈路上都變成新的對(duì)象顯然是最優(yōu)方案。

從實(shí)現(xiàn)角度來說,我們修改一個(gè)層級(jí)很深的節(jié)點(diǎn),一般會(huì)調(diào)用immutable提供的setIn(["a","a"],xx)/update(["a","a"],xxx)這樣的方法。

實(shí)際immutable的整個(gè)一套修改流程是這樣的

假設(shè)我們操作的數(shù)據(jù)是{a:{a:1}} 執(zhí)行 setIn(["a","a"],"XXX")操作

["a","a"]這是一個(gè)keyPath,immutable會(huì)按照順序一層層往里找 找到指定節(jié)點(diǎn)那塊的時(shí)候,開始修改值 得到一個(gè)修改完的{a:xxx}后,再原路向上set每一級(jí),會(huì)先將每一級(jí)淺拷貝一遍,然后更新淺拷貝后的對(duì)象,將修改完的再吐給上一層,重復(fù)這樣的操作,最后返回了一個(gè)新的immutable對(duì)象

// 因?yàn)閛bj在immutable里的存儲(chǔ)格式也是數(shù)組類型(類Map),所以也可以使用arrCopy
function arrCopy(arr, offset) {
  offset = offset || 0;
  var len = Math.max(0, arr.length - offset);
  var newArr = new Array(len);
  for (var ii = 0; ii < len; ii++) {
    newArr[ii] = arr[ii + offset];
  }
  return newArr;
}
// 實(shí)際的更新邏輯
function updateInDeeply(
  inImmutable,
  existing,
  keyPath,
  i,
  notSetValue,
  updater
) {
  const wasNotSet = existing === NOT_SET;
  if (i === keyPath.length) {        //根據(jù)傳進(jìn)的keyPath進(jìn)行迭代
    const existingValue = wasNotSet ? notSetValue : existing;
    const newValue = updater(existingValue); 
    return newValue === existingValue ? existing : newValue;
  }
  if (!wasNotSet && !isDataStructure(existing)) {
    throw new TypeError(
      "Cannot update within non-data-structure value in path [" +
        keyPath.slice(0, i).map(quoteString) +
        "]: " +
        existing
    );
  }
  const key = keyPath[i];
  const nextExisting = wasNotSet ? NOT_SET : get(existing, key, NOT_SET);    //get到每一層的Data
  const nextUpdated = updateInDeeply(
    nextExisting === NOT_SET ? inImmutable : isImmutable(nextExisting),
    nextExisting,
    keyPath,
    i + 1,
    notSetValue,
    updater
  );
  return nextUpdated === nextExisting
    ? existing
    : nextUpdated === NOT_SET
      ? remove(existing, key)
      : set(        //最核心的地方 將change后的結(jié)果set到每一層
          wasNotSet ? (inImmutable ? emptyMap() : {}) : existing,
          key,
          nextUpdated
        );
}
merge/mergeDeep

對(duì)對(duì)象進(jìn)行merge,支持傳入immutable對(duì)象和普通對(duì)象

const objA = Immutable.fromJS({a:1,b:{a:2}})

const objB = Immutable.fromJS({a:3,b:{h:2}})

objA.merge({a:3,b:{h:2}}) // {a:3,b:{h:2}}

objA.merge(objB) // {a:3,b:{h:2}}

objA.mergeDeep({a:3,b:{h:2}}) // {a:3,b:{a:2,h:2}}


// 通常我們r(jià)educer中對(duì)于action,state處理都會(huì)這樣

 return {
     ...state,
     ...action.payload
 }

// 現(xiàn)在我們可以這么寫
 return state.merge(action.payload)
is

對(duì)兩個(gè)immutable對(duì)象進(jìn)行diff

const immutableA = Immutable.fromJS({a:{a:1}})

const immutableB = immutableA.fromJS({a:{a:1}})

immutableA === immutableB // false

is(immutableA, immutableB) //true

is不支持淺immutable Data的對(duì)比,不支持普通對(duì)象的對(duì)比

常用操作

1.List:pop,push,shift,unshift,slice,forEach,Map,filter

與原生用法幾乎一致,但是有兩點(diǎn)需要注意:所有修改型操作必定返回一個(gè)新的Data。foreach是返回迭代數(shù)

Immutable.fromJS([1, 2, 3, 4, 5, {a: 123}]).forEach((value, index, array)=>{
    return value < 5;
}); // 5

2.Map:同時(shí)也支持forEach之類的遍歷,因?yàn)槠浯鎯?chǔ)方式以Array存儲(chǔ)。特有方法的話mapKeys/mapEntries

常用api其實(shí)不想多說,網(wǎng)上有大把的資源 百度 必應(yīng) 谷歌

Hash

將immutable對(duì)象hash化,在其屬性_hash上掛載,

const obj1 = immutable.fromJS({a:{a:1}})
const obj2 = immutable.Map({a:{a:1}})

Immutable.hash(obj1)

Immutable.hash(obj2)

obj1.__hash === obj2.__hash // false 具體原理見下文Hash原理剖析

withMutation&asMutable/asImutable
const ListA = Immutable.List(["a","b"])

ListA.push("gg")
    .pop()
    .shift()

按照immutable每個(gè)操作必定返回新的對(duì)象的這種說法,上述代碼產(chǎn)生了很多冗余的List,而針對(duì)這點(diǎn)immutable給出了兩種解決方案

//withMutation
const ListA = Immutable.List(["a","b"])

const ListB = ListA.withMutations(($list)=>{
    $list.push("gg")
        .pop()
        .shift()
})

//asMutable/asImutable
const ListA = Immutable.List(["a","b"])
const ListB = ListA.asMutable()

console.log(ListA === ListB,Immutable.is(ListA,ListB)) // false true

const ListC = ListB.pop()

console.log(ListB,ListC === ListB,Immutable.is(ListC,ListB)) // ["a"] true true

const ListFinally = ListC.asImmutable()    //asMutable/asImutable必須同時(shí)成對(duì)出現(xiàn)

而immutable是怎么實(shí)現(xiàn)這個(gè)的呢??

仔細(xì)觀察immutable對(duì)象,嗯,你會(huì)發(fā)現(xiàn)有個(gè)__ownerID,嗯,然后呢,就沒有然后了。。。然后你就要看源碼了

//asMutable源碼
function asMutable() {
  return this.__ownerID ? this : this.__ensureOwner(new OwnerID());
}

//當(dāng)我們修改節(jié)點(diǎn)時(shí)都會(huì)類似觸發(fā)一個(gè)editableVNode這樣的函數(shù)
function editableVNode(node, ownerID) {
  if (ownerID && node && ownerID === node.ownerID) {
    return node;
  }
  return new VNode(node ? node.array.slice() : [], ownerID); //
}

通過實(shí)例函數(shù)的方式獲得唯一ID,這點(diǎn)還是很細(xì)膩的

immutable優(yōu)點(diǎn)及使用技巧 1.高效的存取方案 __root + __tail

如果說immutable他要轉(zhuǎn)換一個(gè)length 1000的array,他會(huì)怎么做呢,存儲(chǔ)上他會(huì)將1000按length32為單位進(jìn)行存儲(chǔ),放置在_root中,剩下的扔進(jìn)_tail。同理,immutable在進(jìn)行g(shù)et/set操作時(shí),扔進(jìn)去一個(gè)索引100,首先做的事是,確認(rèn)這個(gè)100在那個(gè)索引區(qū),然后再去那個(gè)32的array中拿數(shù)據(jù)。

// List.set
let newTail = list._tail;
  let newRoot = list._root;
  const didAlter = MakeRef(DID_ALTER);
  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
    );
  }


以32位劃分存儲(chǔ)分區(qū)
const SHIFT = 5;
const SIZE = 1 << SHIFT;
function getTailOffset(size) {
  return size < SIZE ? 0 : ((size - 1) >>> 5) << 5;
}
2.is

is其實(shí)就是immutable中Map/List對(duì)象的deepDiff,而實(shí)際真正的diff過程就是hash與漫長(zhǎng)的迭代diff。如果你對(duì)比的兩個(gè)immutable中,一個(gè)data被hash過,另一個(gè)數(shù)據(jù)又是由其衍生出來的,那diff效率將是最高的

3.Hash算法的原理與優(yōu)化

1.檢測(cè)本地weakMap/stringHashCache中是否存在已hash過當(dāng)前對(duì)象/字符串。

一方面通過WeakMap的弱引用,讓這些作為key的obj可以被gc,另一方面對(duì)于數(shù)據(jù)的hash過程只會(huì)是越來越快

2.對(duì)于immutable Data的特殊對(duì)象如何Hash?如DOMElement,非immutable Obj
對(duì)于DOMElement

首先檢測(cè)是否為IE 低版本 IE對(duì)于每一個(gè)DOM都賦予了唯一的node.uniqueID

function getIENodeHash(node) {
  if (node && node.nodeType > 0) {
    switch (node.nodeType) {
      case 1: // Element
        return node.uniqueID;
      case 9: // Document
        return node.documentElement && node.documentElement.uniqueID;
    }
  }
}

若為非IE

手動(dòng)維護(hù)一個(gè)遞增的hashWeakMap,Symbol私有化后放在prototype中

let UID_HASH_KEY = "__immutablehash__";
if (typeof Symbol === "function") {
  UID_HASH_KEY = Symbol(UID_HASH_KEY);
}
hashed = ++objHashUID;
if (objHashUID & 0x40000000) {
    objHashUID = 0;
}
Object.defineProperty(obj, UID_HASH_KEY, {
  enumerable: false,
  configurable: false,
  writable: false,
  value: hashed,
});

對(duì)于非immutable Data(Map淺immutable后里的深層嵌套數(shù)據(jù))

代碼同上,維護(hù)一個(gè)WeakMap,key是obj,Value是遞增的objHashUID

3.Hash沖突?merge KeyHash+ValueHash

對(duì)于純數(shù)組,immutable的hash方案是hash所有索引下的value然后進(jìn)行疊加

對(duì)于object,immutable對(duì)每一個(gè)object單元以Hash(key)+Hash(value)最后進(jìn)行疊加

function hashCollection(collection) {
  if (collection.size === Infinity) {
    return 0;
  }
  const ordered = isOrdered(collection);
  const keyed = isKeyed(collection);
  let h = ordered ? 1 : 0;
  const size = collection.__iterate(
    keyed
      ? ordered
        ? (v, k) => {
            h = (31 * h + hashMerge(hash(v), hash(k))) | 0;
          }
        : (v, k) => {
            h = (h + hashMerge(hash(v), hash(k))) | 0;
          }
      : ordered
        ? v => {
            h = (31 * h + hash(v)) | 0;
          }
        : v => {
            h = (h + hash(v)) | 0;
          }
  );
  return murmurHashOfSize(size, h);
}

使用技巧

1.盡早提前hash的時(shí)間點(diǎn),在一些ajax請(qǐng)求,launch加載的時(shí)候,這樣在進(jìn)行長(zhǎng)列表render的時(shí)候可以很大程度上優(yōu)化性能,同時(shí)安利一波biz-decorator,集成autobind,debounce,throttle,pureRender裝飾器

2.如果想用hash去做diff,要仔細(xì)考慮immutable是否Deep

Deep&Hash immutable時(shí)間長(zhǎng) 初始hash時(shí)間長(zhǎng) diff速度快(與層次有關(guān))

!Deep&Hash immutable時(shí)間短 初始hash時(shí)間短 diff速度快

!Deep&!Hash immutable時(shí)間短 無hash時(shí)間 diff速度快

結(jié)論:

Deep&Hash 耗時(shí)長(zhǎng),但是可以給hashMap提供更多的hash樣本,前提是這個(gè)數(shù)據(jù)樣本會(huì)頻繁被用到

diff時(shí)無需對(duì)元數(shù)據(jù)衍生出來的數(shù)據(jù)hash化,并不會(huì)優(yōu)化diff時(shí)間

//我們對(duì)一個(gè)5MB的商品數(shù)據(jù)進(jìn)行immutable

const Map = Immutable.Map(MockData) // 3.489013671875ms
Immutable.hash(Map)    // 1.677001953125ms

const fromJS = Immutable.fromJS(MockData) // 962.42724609375ms
Immutable.hash(fromJS)    // 306.51318359375ms

const Map2 = Map.setIn(["data","data",10,"state"],"5");

Immutable.is(Map2,Map) //3.2197265625ms

const fromJS2 = fromJS.setIn(["data","data",10,"state"],"5");

Immutable.is(fromJS2,fromJS) //10.624267578125ms

//相比之前fromJS的Immutable hash 時(shí)間成本節(jié)省了一個(gè)數(shù)量級(jí)
Immutable.hash(fromJS2); //16.772216796875ms

//diff時(shí)間上并沒有顯著的提升
Immutable.is(fromJS2,fromJS) //7.08203125ms


immutable缺點(diǎn)與解決方案

1.請(qǐng)求或存入LS時(shí)都需要轉(zhuǎn)成通用對(duì)象,但是仍然可以使用JSON.stringify,也可以toJS()

2.語法上基本兼容以前api(類ES6 Map/Set),但是寫法上有很大轉(zhuǎn)變(建議新項(xiàng)目或外部依賴較少的項(xiàng)目切immutable)

3.提供api較為基礎(chǔ),或達(dá)不到使用目的,可以在原有基礎(chǔ)上擴(kuò)展

4.基本常用類型多為Map,List,可對(duì)immutable針對(duì)性的閹割,或者自行實(shí)行一套

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/89990.html

相關(guān)文章

  • immer.js 簡(jiǎn)介及源碼解析

    摘要:例如維護(hù)一份在內(nèi)部,來判斷是否有變化,下面這個(gè)例子就是一個(gè)構(gòu)造函數(shù),如果將它的實(shí)例傳入對(duì)象作為第一個(gè)參數(shù),就能夠后面的處理對(duì)象中使用其中的方法上面這個(gè)構(gòu)造函數(shù)相比源代碼省略了很多判斷的部分。 showImg(https://segmentfault.com/img/bV27Dy?w=1400&h=544); 博客鏈接:下一代狀態(tài)管理工具 immer 簡(jiǎn)介及源碼解析 JS 里面的變量類...

    Profeel 評(píng)論0 收藏0
  • 源碼解析 —— Vue的響應(yīng)式數(shù)據(jù)流

    摘要:下面我們會(huì)向大家解釋清楚為什么這個(gè)這么重要,以及它和的響應(yīng)式數(shù)據(jù)流有什么關(guān)系。源碼前面鋪墊這么多就是希望大家能理解接下來要講的響應(yīng)式數(shù)據(jù)流。總結(jié)講到這里大家應(yīng)該都能夠明白的響應(yīng)式數(shù)據(jù)流是如何實(shí)現(xiàn)的。 Vue、React介紹 目前前端社區(qū)比較推崇的框架有Vue 和 React,公司內(nèi)部許多端都自發(fā)的將原有的老技術(shù)方案(widget + jQuery)遷移到 Vue / React上了。我...

    LuDongWei 評(píng)論0 收藏0
  • 如何優(yōu)化你的超大型React應(yīng)用 【原創(chuàng)精讀】

    摘要:往往純的單頁面應(yīng)用一般不會(huì)太復(fù)雜,所以這里不引入和等等,在后面復(fù)雜的跨平臺(tái)應(yīng)用中我會(huì)將那些技術(shù)一擁而上。構(gòu)建極度復(fù)雜,超大數(shù)據(jù)的應(yīng)用。 showImg(https://segmentfault.com/img/bVbvphv?w=1328&h=768); React為了大型應(yīng)用而生,Electron和React-native賦予了它構(gòu)建移動(dòng)端跨平臺(tái)App和桌面應(yīng)用的能力,Taro則賦...

    cfanr 評(píng)論0 收藏0
  • 如何優(yōu)化你的超大型React應(yīng)用 【原創(chuàng)精讀】

    摘要:往往純的單頁面應(yīng)用一般不會(huì)太復(fù)雜,所以這里不引入和等等,在后面復(fù)雜的跨平臺(tái)應(yīng)用中我會(huì)將那些技術(shù)一擁而上。構(gòu)建極度復(fù)雜,超大數(shù)據(jù)的應(yīng)用。 showImg(https://segmentfault.com/img/bVbvphv?w=1328&h=768); React為了大型應(yīng)用而生,Electron和React-native賦予了它構(gòu)建移動(dòng)端跨平臺(tái)App和桌面應(yīng)用的能力,Taro則賦...

    codecook 評(píng)論0 收藏0
  • 如何優(yōu)化你的超大型React應(yīng)用 【原創(chuàng)精讀】

    摘要:往往純的單頁面應(yīng)用一般不會(huì)太復(fù)雜,所以這里不引入和等等,在后面復(fù)雜的跨平臺(tái)應(yīng)用中我會(huì)將那些技術(shù)一擁而上。構(gòu)建極度復(fù)雜,超大數(shù)據(jù)的應(yīng)用。 showImg(https://segmentfault.com/img/bVbvphv?w=1328&h=768); React為了大型應(yīng)用而生,Electron和React-native賦予了它構(gòu)建移動(dòng)端跨平臺(tái)App和桌面應(yīng)用的能力,Taro則賦...

    xiguadada 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<