成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

Memoization in JavaScript

ccj659 / 2209人閱讀

摘要:源碼函數(shù)調(diào)用過,沒有變化,參數(shù)時(shí)返回緩存值。而通過,可以把上一次的計(jì)算結(jié)果保存下來,而避免重復(fù)計(jì)算。這意味著將跳過渲染組件,并重用最后渲染的結(jié)果。

1. 基本概念

在一個(gè)CPU密集型應(yīng)用中,我們可以使用Memoization來進(jìn)行優(yōu)化,其主要用于通過存儲昂貴的函數(shù)調(diào)用的結(jié)果來加速程序,并在再次發(fā)生相同的輸入時(shí)返回緩存的結(jié)果。
例如一個(gè)簡單的求平方根的函數(shù):

const sqrt = Math.sqrt;
//使用cache緩存
const sqrt = (arg)=>{
    if(!sqrt.cache){
        sqrt.cache = {};
    }
    if(!sqrt.cache[arg]){
        sqrt.cache[arg] = Math.sqrt(arg)
    }
    return sqrt.cache[arg]
}
//簡單的運(yùn)行時(shí)間對比
//第一次運(yùn)行:
console.time("start1")
sqrt(779)
console.timeEnd("start1")
VM516:3 start1: 0.01806640625ms
//第二次運(yùn)行:
console.time("start1")
sqrt(779)
console.timeEnd("start1")
VM521:3 start1: 0.005859375ms
2. 簡單通用實(shí)現(xiàn)

我們實(shí)現(xiàn)一個(gè)通用的memoize函數(shù),用它來包裝任意純函數(shù),并緩存其計(jì)算結(jié)果。

function memoize(fn){
        return function(){
            var args = Array.prototype.slice.call(arguments);
            fn.cache = fn.cache || {};
            return fn.cache[args] ?
                 fn.cache[args] :(fn.cache[args] = fn.apply(this,args))
        }
}

必須注意的是,memoize的原理是在發(fā)生相同的輸入時(shí)返回緩存的結(jié)果,這意味著其包裝的函數(shù)應(yīng)當(dāng)是一個(gè)純函數(shù),當(dāng)函數(shù)不純時(shí),相同的輸入在不同時(shí)間可能返回不同的結(jié)果,此時(shí)再使用memoize明顯不合時(shí)宜。

3. 常見的memoize庫 3.1 lodash庫中的memoize

在該庫中,使用Map緩存計(jì)算結(jié)果,支持傳入resolver函數(shù)將傳入的參數(shù)轉(zhuǎn)換為存入Map的鍵名(默認(rèn)鍵名是第一個(gè)參數(shù),當(dāng)鍵名是引用類型時(shí),引用不變,緩存不會更新)。

function memoize(func, resolver) {
  if (typeof func != "function" || (resolver != null && typeof resolver != "function")) {
    throw new TypeError("Expected a function")
  }
  const memoized = function(...args) {
    const key = resolver ? resolver.apply(this, args) : args[0]
    const cache = memoized.cache

    if (cache.has(key)) {
      return cache.get(key)
    }
    const result = func.apply(this, args)
    memoized.cache = cache.set(key, result) || cache
    return result
  }
  memoized.cache = new (memoize.Cache || Map)
  return memoized
}

memoize.Cache = Map
3.2 memoize-one

與lodash不同,memoize-one僅僅保存上一次調(diào)用時(shí)的結(jié)果,如果下次參數(shù)變化,則更新緩存。因此不必?fù)?dān)心由于maxAge, maxSize, exclusions等導(dǎo)致的內(nèi)存泄漏。

//源碼
export default function mixed>(
  resultFn: ResultFn,
  isEqual?: EqualityFn = areInputsEqual,
): ResultFn {
  let lastThis: mixed;
  let lastArgs: mixed[] = [];
  let lastResult: mixed;
  let calledOnce: boolean = false;
//函數(shù)調(diào)用過,this沒有變化,參數(shù)isEqual時(shí)返回緩存值。
  const result = function(...newArgs: mixed[]) {
    if (calledOnce && lastThis === this && isEqual(newArgs, lastArgs)) {
      return lastResult;
    }
    lastResult = resultFn.apply(this, newArgs);
    calledOnce = true;
    lastThis = this;
    lastArgs = newArgs;
    return lastResult;
  };

  return (result: any);
}

可以看到,可以通過第二個(gè)參數(shù)自定義參數(shù)是否相同,默認(rèn)是areInputsEqual函數(shù)。

//areInputsEqual實(shí)現(xiàn)
export default function areInputsEqual(
  newInputs: mixed[],
  lastInputs: mixed[],
) {
//先進(jìn)行參數(shù)長度比較
  if (newInputs.length !== lastInputs.length) {
    return false;
  }
 //參數(shù)淺比較
  for (let i = 0; i < newInputs.length; i++) {
    if (newInputs[i] !== lastInputs[i]) {
      return false;
    }
  }
  return true;
}

可以自定義參數(shù)比較函數(shù)進(jìn)行深比較

import memoizeOne from "memoize-one";
import isDeepEqual from "lodash.isequal";

const identity = x => x;

const shallowMemoized = memoizeOne(identity);
const deepMemoized = memoizeOne(identity, isDeepEqual);

const result1 = shallowMemoized({ foo: "bar" });
const result2 = shallowMemoized({ foo: "bar" });

result1 === result2; // false - difference reference

const result3 = deepMemoized({ foo: "bar" });
const result4 = deepMemoized({ foo: "bar" });

result3 === result4; // true - arguments are deep equal
4. memoize-one在React中的應(yīng)用

在React中有這樣一個(gè)應(yīng)用場景,當(dāng)部分props變化需要改變派生state時(shí),可以在getDerivedStateFromProps中通過if判斷是否需要重新計(jì)算,但它比它需要的更復(fù)雜,因?yàn)樗仨毝鄮Ц櫤蜋z測每個(gè)props和state的變化,當(dāng)props很多時(shí),這種判斷方式會變得糾纏不清。

class Example extends Component {
  state = {
    filterText: "",
  };
  static getDerivedStateFromProps(props, state) {
    if (
      props.list !== state.prevPropsList ||
      state.prevFilterText !== state.filterText
    ) {
      return {
        prevPropsList: props.list,
        prevFilterText: state.filterText,
        filteredList: props.list.filter(item => item.text.includes(state.filterText))
      };
    }
    return null;
  }
  handleChange = event => {
    this.setState({ filterText: event.target.value });
  };
  render() {
    return (
      
        
        
    {this.state.filteredList.map(item =>
  • {item.text}
  • )}
); } }

而通過Memoization,可以把上一次的計(jì)算結(jié)果保存下來,而避免重復(fù)計(jì)算。例如以下通過memoize-one庫實(shí)現(xiàn)的記憶,可以讓我們不用手動(dòng)判斷l(xiāng)ist, filterText是否變化,是否需要重新計(jì)算。

import memoize from "memoize-one";
class Example extends Component {
  // State only needs to hold the current filter text value:
  state = { filterText: "" };
  // Re-run the filter whenever the list array or filter text changes:
  filter = memoize(
    (list, filterText) => list.filter(item => item.text.includes(filterText))
  );

  handleChange = event => {
    this.setState({ filterText: event.target.value });
  };

  render() {
    const filteredList = this.filter(this.props.list, this.state.filterText);
    return (
      
        
        
    {filteredList.map(item =>
  • {item.text}
  • )}
); } }
5. 配合Redux使用的memoize庫——reselect

在react中,每當(dāng)組件重新渲染,計(jì)算派生狀態(tài)的邏輯就會執(zhí)行一遍(不管這些邏輯是放在render或者是放在getDerivedStateFromProps中,如果沒有采用很多if限制的話)。上節(jié)介紹了使用memoize-one來緩存來避免重復(fù)計(jì)算,當(dāng)我們使用redux時(shí),通常在mapStateToProps中計(jì)算派生狀態(tài),每當(dāng)store中的任意state更新時(shí),都會觸發(fā)mapStateToProps中的計(jì)算,然而,往往派生state通常只依賴部分state,不必每次都計(jì)算。
Reselect是一個(gè)十分貼近redux的memoize庫,其和memoize-one一樣只緩存前一次的計(jì)算值,并支持自定義memoize函數(shù),自定義參數(shù)比較函數(shù)等;其輸入?yún)?shù)由inputSelectors functions 產(chǎn)生,生成的的依然是inputSelectors functions,這意味的與memoize相比,這可以很容易的組合。
另外,mapStateProps的第二個(gè)參數(shù)ownProps也可以傳入selector中。

import { createSelector } from "reselect"

fSelector = createSelector(
    a => state.a,
    b => state.b,
    (a, b) => f(a, b)
)
hSelector = createSelector(
    b => state.b,
    c => state.c,
    (b, c) => h(b, c)
)
gSelector =  createSelector(
    a => state.a,
    c => state.c,
    (a, c) => g(a, c)
)
uSelector = createSelector(
    a => state.a,
    b => state.b,
    c => state.c,
    (a, b, c) => u(a, b, c)
)

...
function mapStateToProps(state) {
    const { a, b, c } = state
    return {
        a,
        b,
        c,
        fab: fSelector(state),
        hbc: hSelector(state),
        gac: gSelector(state),
        uabc: uSelector(state)
    }
}
6. react中memoize原生支持——React.memo

如果你的函數(shù)組件在給定相同的props的情況下呈現(xiàn)相同的結(jié)果,你可以React.memo通過記憶結(jié)果將它包裝在一些調(diào)用中以提高性能。這意味著React將跳過渲染組件,并重用最后渲染的結(jié)果。

默認(rèn)情況下,它只會淺顯比較props對象中的復(fù)雜對象。如果要控制比較,還可以提供自定義比較函數(shù)作為第二個(gè)參數(shù)。

function MyComponent(props) {
  /* render using props */
}
function areEqual(prevProps, nextProps) {
  /*
  return true if passing nextProps to render would return
  the same result as passing prevProps to render,
  otherwise return false
  */
}
export default React.memo(MyComponent, areEqual);

You Probably Don’t Need Derived State - React Blog
Understanding Memoization in JavaScript to Improve Performance
性能優(yōu)化:memoization | Taobao FED | 淘寶前端團(tuán)隊(duì)
lodash/memoize.js at master · lodash/lodash · GitHub
GitHub - alexreardon/memoize-one: A memoization library which only remembers the latest invocation
為什么我們需要reselect - 從0開始實(shí)現(xiàn)react技術(shù)棧 - SegmentFault 思否
React Hooks: Memoization – Sandro Dolidze – Medium

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

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/104002.html

相關(guān)文章

  • JavaScript 高級技巧 Memoization

    摘要:來源于拉丁語,不要與混淆了。本文首先介紹一個(gè)簡單的使用優(yōu)化技術(shù)的例子,然后解讀和庫中使用的源碼,加深理解??偨Y(jié)是一種優(yōu)化技術(shù),避免一些不必要的重復(fù)計(jì)算,可以提高計(jì)算速度。 memoization 來源于拉丁語 memorandum (to be remembered),不要與 memorization 混淆了。 首先來看一下維基百科的描述: In computing, memoizat...

    劉德剛 評論0 收藏0
  • 斐波那契數(shù)列求和的js方案以及優(yōu)化

    摘要:在上做了一道斐波那契數(shù)列求和的題目,做完之后做了一些簡單的優(yōu)化和用另一種方法實(shí)現(xiàn)。動(dòng)態(tài)規(guī)劃解決方案斐波那契數(shù)列求和除了可以用遞歸的方法解決,還可以用動(dòng)態(tài)規(guī)劃的方法解決。 在codewars上做了一道斐波那契數(shù)列求和的題目,做完之后做了一些簡單的優(yōu)化和用另一種方法實(shí)現(xiàn)。 題目 function fibonacci(n) { if(n==0 || n == 1) r...

    xinhaip 評論0 收藏0
  • JS專題之memoization

    摘要:前言在計(jì)算機(jī)領(lǐng)域,記憶是主要用于加速程序計(jì)算的一種優(yōu)化技術(shù),它使得函數(shù)避免重復(fù)演算之前已被處理過的輸入,而返回已緩存的結(jié)果。被執(zhí)行了不是素?cái)?shù),其他數(shù)字默認(rèn)是素?cái)?shù)。我們可以看出,如果從開始打印斐波那契數(shù)列,函數(shù)被執(zhí)行了次。 前言 在計(jì)算機(jī)領(lǐng)域,記憶(memoization)是主要用于加速程序計(jì)算的一種優(yōu)化技術(shù),它使得函數(shù)避免重復(fù)演算之前已被處理過的輸入,而返回已緩存的結(jié)果。 -- wi...

    zhisheng 評論0 收藏0
  • Python 2.7終結(jié)于7個(gè)月后,這是你需要了解的3.X炫酷新特性

    摘要:截止到月號上午點(diǎn),將終結(jié)于在這一段時(shí)間中,很多優(yōu)秀開源項(xiàng)目與庫已經(jīng)停止了對的支持。除了,還提供了一種通過進(jìn)行字符串插入的靈活方法。擴(kuò)展的可迭代對象解包最低版本為對于這個(gè)特性,代碼就說明了一切。從 3.0 到 3.8,Python 3 已經(jīng)更新了一波又一波,但似乎我們用起來和 2.7 沒有太大區(qū)別?以前該怎么寫 2.7 的代碼現(xiàn)在就怎么寫,只不過少數(shù)表達(dá)方式變了而已。在這篇文章中,作者介紹了 ...

    番茄西紅柿 評論0 收藏0
  • Python 2.7終結(jié)于7個(gè)月后,這是你需要了解的3.X炫酷新特性

    摘要:截止到月號上午點(diǎn),將終結(jié)于在這一段時(shí)間中,很多優(yōu)秀開源項(xiàng)目與庫已經(jīng)停止了對的支持。除了,還提供了一種通過進(jìn)行字符串插入的靈活方法。擴(kuò)展的可迭代對象解包最低版本為對于這個(gè)特性,代碼就說明了一切。從 3.0 到 3.8,Python 3 已經(jīng)更新了一波又一波,但似乎我們用起來和 2.7 沒有太大區(qū)別?以前該怎么寫 2.7 的代碼現(xiàn)在就怎么寫,只不過少數(shù)表達(dá)方式變了而已。在這篇文章中,作者介紹了 ...

    chadLi 評論0 收藏0

發(fā)表評論

0條評論

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