Created
December 22, 2016 16:51
-
-
Save FWeinb/33a1b04548e0cdcc89dad0c48fcf4e01 to your computer and use it in GitHub Desktop.
Proxy path reader (http://jsbench.github.io/#33a1b04548e0cdcc89dad0c48fcf4e01) #jsbench #jsperf
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="utf-8"/> | |
<title>Proxy path reader</title> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/benchmark/1.0.0/benchmark.min.js"></script> | |
<script src="./suite.js"></script> | |
</head> | |
<body> | |
<h1>Open the console to view the results</h1> | |
<h2><code>cmd + alt + j</code> or <code>ctrl + alt + j</code></h2> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"use strict"; | |
(function (factory) { | |
if (typeof Benchmark !== "undefined") { | |
factory(Benchmark); | |
} else { | |
factory(require("benchmark")); | |
} | |
})(function (Benchmark) { | |
var suite = new Benchmark.Suite; | |
Benchmark.prototype.setup = function () { | |
// Path resolver factory | |
// Creates a resolver based on the target, path and resolveValue function | |
const createPathResolver = (target, path, resolveValue) => (context) => { | |
const resolvedPath = []; | |
// resolvePath will contain the actual path by resolving recursively | |
for (let i = 0; i < path.length; i++) { | |
const curr = path[i] | |
resolvedPath.push( | |
typeof curr === 'function' | |
? curr(context).value | |
: curr | |
); | |
} | |
return { | |
target, | |
path: resolvedPath.join('.'), | |
value: resolveValue(resolvedPath, context) | |
} | |
} | |
// Used to transfere a function from toPrimitive to the "parent" object | |
let fnTransfer; | |
function PathRecorder (target, resolveValue) { | |
// Create the root function the proxy is wrapping for the main object | |
const rootFunction = () => { | |
const f = () => {}; | |
f.path = []; | |
f.root = true; | |
return f; | |
} | |
// Create a child function the proxy is wrapping for all supsequent objects | |
const childFunction = (path) => { | |
const f = () => {}; | |
f.path = path; | |
return f; | |
} | |
const toPrimitive = () => ''; | |
// Handler of all Proxy object | |
const Handler = { | |
apply(obj, thisArg, values) { | |
// Root is called like a tagged template use old behaviour | |
if (obj.root) return operators[target].apply(null, values); | |
// Cache the function | |
obj.cachedFn = obj.cachedFn || createPathResolver(target, obj.path, resolveValue); | |
// context is the first parameter | |
const context = values[0]; | |
// Resolve the value and return it. | |
return obj.cachedFn(context); | |
}, | |
get(obj, name, proxy) { | |
// TODO: This needs to be changed in string`` in cerebral | |
// because this could possible go wrong | |
if (name === 'getValue') return undefined; | |
// cache busting | |
obj.cachedFn == undefined; | |
// Always start a new path for a root obj | |
obj.path = obj.root ? obj.path = [] : obj.path; | |
if (name === Symbol.toPrimitive) { | |
// create a resolver that will be transfered to the "parent" object | |
fnTransfer = createPathResolver(target, obj.path, resolveValue); | |
return toPrimitive; | |
} else if (fnTransfer !== undefined) { | |
// There is a fnTransfere function add it. | |
obj.path.push(fnTransfer); | |
fnTransfer = undefined; | |
} else { | |
// input.a.b normal property access just append | |
obj.path.push(name); | |
} | |
// Only create a new function when we are on the root object, otherwise just reuse it. | |
return new Proxy(obj.root ? childFunction(obj.path) : obj, Handler); | |
} | |
}; | |
return new Proxy(rootFunction(), Handler); | |
} | |
const state = PathRecorder('state', (path, context) => { | |
return path.reduce((currentState, key) => { | |
return currentState ? currentState[key] : undefined | |
}, context.state) | |
}); | |
const input = PathRecorder('input', (path, context) => { | |
return path.reduce((currentState, key) => { | |
return currentState ? currentState[key] : undefined | |
}, context.input) | |
}); | |
const signal = PathRecorder('signal', (path, context) => { | |
return context.controller.getSignal(path); | |
}); | |
const context = { | |
input: { | |
awesome: { | |
hello: "World" | |
} | |
}, | |
state: { | |
hello: { | |
a: 'hello', | |
world: { | |
'World' : "Awesome!!!!" | |
} | |
} | |
} | |
} | |
}; | |
suite.add("state.hello.world[input.awesome[state.hello.a]](context);", function () { | |
state.hello.world[input.awesome[state.hello.a]](context); | |
}); | |
suite.on("cycle", function (evt) { | |
console.log(" - " + evt.target); | |
}); | |
suite.on("complete", function (evt) { | |
console.log(new Array(30).join("-")); | |
var results = evt.currentTarget.sort(function (a, b) { | |
return b.hz - a.hz; | |
}); | |
results.forEach(function (item) { | |
console.log((idx + 1) + ". " + item); | |
}); | |
}); | |
console.log("Proxy path reader"); | |
console.log(new Array(30).join("-")); | |
suite.run(); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment