3 ms·
I wonder if using structuredClone() has a performance penalty compared to a simple block-scoped closure though? let doFn, undoFn; {const newStroke = current
by neRok 2y ago
I wonder if using structuredClone() has a performance penalty compared to a simple block-scoped closure though?
let doFn, undoFn; {const newStroke = currentStroke; doFn = ()=>strokes.push(newStroke); undoFn = ()=>strokes.pop();}
- noduerme 2y agoI'm sure it has a massive performance penalty, but probably not as much as other runtime ways of deep-copying. It's doing something different than your code above. In the code above, `newStroke` just makes a reference to `currentStroke`. Nothing is changed by creating a `const newStroke` and pushing it to the array. You're just pushing a reference to the original. In the OP's example, `currentStroke` is something that gets modified. If you used the code above, as soon as you modify `currentStroke`, every reference in the `strokes` array will change. The fact that you used `let` instead of `var` in the loop means you are creating a new constant, but that constant isn't preserving a snapshot of `currentStroke` unless you explicitly make it a new object. Otherwise it's just a reference. You need to deep-clone it in the undo function in order to preserve its previous state. Something like: `let previousStroke = {a:currentStroke.a,b:currentStroke.b}` where `a` and `b` are primitives that are copied, not objects that are referenced, before you change `currentStroke`. If you did it manually, you'd have to keep keep recursively checking `a` and `b` all the way down to their primitives, and rebuild the object. An easy way of doing this for objects without functions or classes in them is just `const previousStroke = JSON.parse(JSON.stringify(currentStroke));` but usually I've had to write custom re-initializers if for example `previousStroke.a` was a class instance.
- neRok 2y ago> Nothing is changed by creating a `const newStroke` and pushing it to the array. Not true. If you look carefully you will see the `const` and everything after it is wrapped in curly braces, and those braces create a block... > Traditionally (before ES6)... blocks with curly braces do not create scopes... [but] blocks are finally treated as scopes in ES6, but only if you declare variables with let or const. — https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Closures https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guid... So the curly braces with the use of const/let creates a new lexical scope, hence solving the issue. You can test it in the browser console to confirm; var stack=[], cmd=""; cmd="success"; stack.push(()=>cmd); cmd="fail"; stack.push(()=>cmd); stack[0](); /* returns "fail" */ var stack=[], cmd=""; cmd="success"; {const c=cmd; stack.push(()=>c);} cmd="fail"; {const c=cmd; stack.push(()=>c);} stack[0](); /* returns "success" */ It's probably not a good idea to do it this way, because it's easily "overlooked". I would probably use a class or factory-function if I had the need.
- julik 2y agoTIL