mirror of
https://github.com/ianstormtaylor/slate.git
synced 2025-08-27 00:54:22 +02:00
Implement memoize using native Maps
It should offer better performances, by avoiding comparing values using Immutable.is, and removing the small immutable object creation overhead.
This commit is contained in:
@@ -1,10 +1,8 @@
|
|||||||
|
|
||||||
import { Map } from 'immutable'
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The leaf node of a cache tree.
|
* The leaf node of a cache tree. Used to support variable argument length.
|
||||||
*
|
*
|
||||||
* An object, so that immutable maps will key it by reference.
|
* A unique object, so that native Maps will key it by reference.
|
||||||
*
|
*
|
||||||
* @type {Object}
|
* @type {Object}
|
||||||
*/
|
*/
|
||||||
@@ -12,12 +10,19 @@ import { Map } from 'immutable'
|
|||||||
const LEAF = {}
|
const LEAF = {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An unique value used to detect cache misses
|
* A value to represent a memoized undefined value. Allows efficient
|
||||||
|
* value retrieval using Map.get only.
|
||||||
*
|
*
|
||||||
* @type {Object}
|
* @type {Object}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const NO_SET = {}
|
const UNDEFINED = {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default value for unset keys in native Maps
|
||||||
|
*/
|
||||||
|
|
||||||
|
const UNSET = undefined
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Memoize all of the `properties` on a `object`.
|
* Memoize all of the `properties` on a `object`.
|
||||||
@@ -36,23 +41,78 @@ function memoize(object, properties) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
object[property] = function (...args) {
|
object[property] = function (...args) {
|
||||||
const keys = [property, ...args, LEAF]
|
const keys = [property, ...args]
|
||||||
this.__cache = this.__cache || new Map()
|
this.__cache = this.__cache || new Map()
|
||||||
|
|
||||||
const cachedValue = this.__cache.getIn(keys, NO_SET)
|
const cachedValue = getIn(this.__cache, keys)
|
||||||
if (cachedValue !== NO_SET) {
|
if (cachedValue !== UNSET) {
|
||||||
return cachedValue
|
return cachedValue === UNDEFINED ? undefined : cachedValue
|
||||||
}
|
}
|
||||||
|
|
||||||
const value = original.apply(this, args)
|
const value = original.apply(this, args)
|
||||||
// If `original` is recursive, it might have changed the cache,
|
this.__cache = setIn(this.__cache, keys, value)
|
||||||
// so read it from this.__cache
|
|
||||||
this.__cache = this.__cache.setIn(keys, value)
|
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a value at a key path in a tree of Map, creating Maps on the go.
|
||||||
|
*
|
||||||
|
* @param{Map} map
|
||||||
|
* @param{Array} keys
|
||||||
|
* @param{Any} value
|
||||||
|
* @return {Map}
|
||||||
|
*/
|
||||||
|
|
||||||
|
function setIn(map, keys, value) {
|
||||||
|
value = value === undefined ? UNDEFINED : value
|
||||||
|
|
||||||
|
let parentMap = map
|
||||||
|
let childMap
|
||||||
|
for (const key of keys) {
|
||||||
|
childMap = parentMap.get(key)
|
||||||
|
|
||||||
|
if (childMap === UNSET) {
|
||||||
|
// This path was not created yet
|
||||||
|
childMap = new Map()
|
||||||
|
parentMap.set(key, childMap)
|
||||||
|
}
|
||||||
|
|
||||||
|
parentMap = childMap
|
||||||
|
}
|
||||||
|
// The whole map path was created
|
||||||
|
|
||||||
|
// Set the value to the bottom most map
|
||||||
|
childMap.set(LEAF, value)
|
||||||
|
|
||||||
|
return map
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a value at a key path in a tree of Map.
|
||||||
|
* If not set, returns UNSET.
|
||||||
|
* If the set value is undefined, returns UNDEFINED
|
||||||
|
*
|
||||||
|
* @param{Map} map
|
||||||
|
* @param{Array} keys
|
||||||
|
* @return {Any | UNSET | UNDEFINED}
|
||||||
|
*/
|
||||||
|
|
||||||
|
function getIn(map, keys) {
|
||||||
|
let childMap
|
||||||
|
for (const key of keys) {
|
||||||
|
childMap = map.get(key)
|
||||||
|
if (childMap === UNSET) {
|
||||||
|
// Not found
|
||||||
|
return UNSET
|
||||||
|
}
|
||||||
|
map = childMap
|
||||||
|
}
|
||||||
|
|
||||||
|
return childMap.get(LEAF)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Export.
|
* Export.
|
||||||
*/
|
*/
|
||||||
|
Reference in New Issue
Block a user