From aec8289f1167e1f8645a5f4a49d3ca85c99bbd87 Mon Sep 17 00:00:00 2001 From: Yuri Tatishchev Date: Thu, 16 Apr 2026 16:15:22 -0700 Subject: [PATCH] lab15: init --- lab15/smart-array.js | 86 ++++++++++++++++++++++++++++++++++++++++++++ lab15/tracing.js | 21 +++++++++++ lab15/undoable.js | 47 ++++++++++++++++++++++++ 3 files changed, 154 insertions(+) create mode 100644 lab15/smart-array.js create mode 100644 lab15/tracing.js create mode 100644 lab15/undoable.js diff --git a/lab15/smart-array.js b/lab15/smart-array.js new file mode 100644 index 0000000..03d6ab7 --- /dev/null +++ b/lab15/smart-array.js @@ -0,0 +1,86 @@ +"use strict"; + +// Matches paterns like '3-10' +const RANGE_PAT = /^(\d+)-(\d+)$/; + +// Matches negative index values +const FROM_END_PAT = /^-(\d+)$/; + +const NUM_PAT = /^-?\d+$/; + +function SmartArray(...args) { + return new Proxy(args, { + get: function(target, prop) { + if (prop.match(RANGE_PAT)) { + // Return a subarray of the elements in the specified range, + // INCLUDING the specified end index. + let start = parseInt(prop.replace(RANGE_PAT, "$1")); + let end = parseInt(prop.replace(RANGE_PAT, "$2")) + 1; + return target.slice(start, end); + } else if (prop.match(FROM_END_PAT)) { + // + // ***YOUR CODE HERE*** + // + // Return the element at the specified position, counting + // back from target.length. So "-1" will refer to the last + // element in the array. + // + // If the resulting index position is negative, + // raise an exception. + } else { + // Do the usual array thing -- get the value at the specified index. + return Reflect.get(...arguments); + } + }, + set: function(target, prop, newVal) { + // + // ***YOUR CODE HERE*** + // + // For smart arrays, we only allow updates to numerical fields. + // Throw an exception for a 'prop' that is not a valid integer. + // + // If prop is zero or positive, update the array position normally. + // + // If negative, update the position counting from the end of the array. + // However, raise an exception if the resulting index is still negative. + }, + }); +} + +let arr = SmartArray('a', 'b', 'c', 'd', 'e', 'f'); + +console.log(arr[0]); // a +console.log(arr[4]); // e +console.log(arr['hello']); // undefined + +console.log(arr['2-4']); // [c,d,E] +console.log(arr['3-5']); // [d,E,f] + +console.log(arr[-1]); // f +console.log(arr[-3]); // d + +try { + console.log(arr[-99]); +} catch (e) { + console.log("Exception correctly thrown."); +} + +arr[1] = 'B'; +console.log(arr[1]); // B + +arr[-2] = 'E'; +console.log(arr[4]); // E + +try { + arr['2-4'] = 'hello'; +} catch (e) { + console.log("Exception correctly thrown."); +} + +try { + arr[3*"hello"] = 'hello'; +} catch (e) { + console.log("Exception correctly thrown."); +} + +console.log(arr); diff --git a/lab15/tracing.js b/lab15/tracing.js new file mode 100644 index 0000000..6917dc4 --- /dev/null +++ b/lab15/tracing.js @@ -0,0 +1,21 @@ +let obj = { foo: 'bar' }; + +let o = new Proxy(obj, { + set: (target, name, val) => { + console.log(`Setting ${name} to ${val}`); + //target[name] = val; + //return true; + //return Reflect.set(target,name,val); + return Reflect.set(...arguments); + }, + get: (target, name) => { + console.log(`Getting ${name}`); + return Reflect.get(...arguments); + } +}); + +o.foo = "fighters"; + +let ff = "foo" + o.foo; + + diff --git a/lab15/undoable.js b/lab15/undoable.js new file mode 100644 index 0000000..b0b9325 --- /dev/null +++ b/lab15/undoable.js @@ -0,0 +1,47 @@ +"use strict"; + +// Input: any object. +// +// Returns: An object with a 'revert' method that reverts +// any changes to the specified field. +function makeUndoable(o) { + let oldVals = {}; + return new Proxy(o, { + get: (target, name) => { + // Adding a 'revert' method on undoable objects, + // which is not part of the original object. + if (name === 'revert') { + return function(p) { + target[p] = oldVals[p]; + } + // If any property other than 'revert' is requested, + // return the value from the underlying object. + } else return target[name]; + }, + set: (target, name, val) => { + oldVals[name] = target[name]; + o[name] = val; + // Returns true to indicate that assignment was successful. + return true; + }, + deleteProperty: (target, name) => { + oldVals[name] = target[name]; + return delete target[name]; + }, + }); +} + +let emp = { fname: "Joe", lname: "Smith", id: 1234, salary: 100 }; +emp = makeUndoable(emp); + +emp.fname = "Joey"; +console.log(`Joe's name accidentally changed to ${emp.fname}`); + +emp.revert('fname'); +console.log(`Joe's name changed back to ${emp.fname}`); + +delete emp.lname; +console.log(`${emp.fname}'s last name is now ${emp.lname}`); +emp.revert('lname'); +console.log(`${emp.fname}'s last name is now ${emp.lname}`); +