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:
@@ -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.
|
||||
*/
|
||||
|
Reference in New Issue
Block a user