1
0
mirror of https://github.com/ianstormtaylor/slate.git synced 2025-08-26 16:44: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:
Soreine
2016-11-03 11:36:18 +01:00
parent 4cc4535074
commit ce44724944

View File

@@ -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}
*/
@@ -12,12 +10,19 @@ import { Map } from 'immutable'
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}
*/
const NO_SET = {}
const UNDEFINED = {}
/**
* Default value for unset keys in native Maps
*/
const UNSET = undefined
/**
* Memoize all of the `properties` on a `object`.
@@ -36,23 +41,78 @@ function memoize(object, properties) {
}
object[property] = function (...args) {
const keys = [property, ...args, LEAF]
const keys = [property, ...args]
this.__cache = this.__cache || new Map()
const cachedValue = this.__cache.getIn(keys, NO_SET)
if (cachedValue !== NO_SET) {
return cachedValue
const cachedValue = getIn(this.__cache, keys)
if (cachedValue !== UNSET) {
return cachedValue === UNDEFINED ? undefined : cachedValue
}
const value = original.apply(this, args)
// If `original` is recursive, it might have changed the cache,
// so read it from this.__cache
this.__cache = this.__cache.setIn(keys, value)
this.__cache = setIn(this.__cache, keys, 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.
*/