Skip to content

Instantly share code, notes, and snippets.

@FWeinb
Created December 22, 2016 16:51
Show Gist options
  • Save FWeinb/33a1b04548e0cdcc89dad0c48fcf4e01 to your computer and use it in GitHub Desktop.
Save FWeinb/33a1b04548e0cdcc89dad0c48fcf4e01 to your computer and use it in GitHub Desktop.
Proxy path reader (http://jsbench.github.io/#33a1b04548e0cdcc89dad0c48fcf4e01) #jsbench #jsperf
<!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>
"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