Skip to content

Instantly share code, notes, and snippets.

@ochafik
Created March 8, 2018 00:23
Show Gist options
  • Select an option

  • Save ochafik/44471eddd86f3ddcee32ecaf124512e4 to your computer and use it in GitHub Desktop.

Select an option

Save ochafik/44471eddd86f3ddcee32ecaf124512e4 to your computer and use it in GitHub Desktop.
Deconstruct ECMAScript 6 Proxy-based Lenses
<div id="root"></div>
<script>
const {makeLens, getPath, getSubLenses} = (() => {
const getPathSymbol = Symbol('get lens path')
const getSubLensesSymbol = Symbol('get sublenses')
function makeLens(target = makeLensNode()) {
return new Proxy(target, lensHandler)
}
function makeLensNode(path = []) {
return Object.assign(function() {}, {
path,
subLenses: {},
subLens(fragment) {
const subLens = makeLens(makeLensNode(this.path.concat(fragment)))
this.subLenses[fragment] = subLens
return subLens
}
})
}
const lensHandler = {
get(target, propName, receiver) {
if (propName === getPathSymbol) return target.path
if (propName === getSubLensesSymbol) return target.subLenses
if (propName === Symbol.iterator) {
return function*ee() {
let index = 0;
while (true) {
yield target.subLens(index++)
}
}
}
return target.subLens(propName)
},
apply({path}, thisArgument, args) {
let [target, value] = args
if (args.length == 1) {
for (var i = 0, n = path.length; i < n && target; i++) {
const fragment = path[i]
target = target[fragment]
}
return target
} else if (args.length == 2) {
for (var i = 0, n = path.length; i < n - 1; i++) {
const fragment = path[i]
const subTarget = target[fragment]
if (!subTarget) {
target[fragment] = subTarget = typeof fragment === 'number' ? [] : {}
}
target = subTarget
}
return target[path[n - 1]] = value
} else {
throw `Lens expected 1 argument for get and 2 for set, got ${args.length}`
}
}
}
const lenses = makeLens(makeLensNode())
return {
makeLens: () => makeLens(),
getPath(lens) {
return lens[getPathSymbol]
},
getSubLenses(lens) {
return lens[getSubLensesSymbol]
}
}
})();
const lenses = makeLens()
let {
a,
b: {
c: d,
e: [f, g, h],
[Symbol()]: i
}
} = lenses
const data = {
a: 1,
b: {
c: 2,
e: [3]
}
}
function log(l) {
console.log(l, getPath(l), getSubLenses(l), l(data))
l(data, 'replaced')
// console.log(l(data), data)
}
// log(a)
// log(b)
// log(c)
log(d)
// log(e)
log(f)
log(g)
log(i)
console.log(getSubLenses(lenses))
console.log(data)
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment