-
-
Save ochafik/44471eddd86f3ddcee32ecaf124512e4 to your computer and use it in GitHub Desktop.
Deconstruct ECMAScript 6 Proxy-based Lenses
This file contains hidden or 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
| <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