提问人:Scandidi 提问时间:11/16/2023 最后编辑:Peter SeligerScandidi 更新时间:11/19/2023 访问量:143
有没有办法跟踪对象属性的值历史,比如记住和回忆它最近的任何值?
Is there a way to keep track of an object-property's value-history, like memorizing and recalling any of its recent values?
问:
假设我有一个根据当前值更改值的方法。我在这里就让它变得非常简单:
changeValue(){
if(!this.value){
this.value = 'value1'
}
else if(this.value === 'value1'){
this.value = 'value2'
}
else if(this.value === 'value2'){
this.value = 'value3'
}
},
然后我还有另一种方法,如果当前值为 2 或 3,则更改为第四个值
changeValues(){
if(this.value === 'value2' || this.value === 'value3'){
this.value = 'valueX'
}
},
现在,我想添加一个单击事件,将 valueX 的值更改回以前的值,但我无法在代码中指定它,因为以前的值可以是 value2 或 value3。
有没有办法查看变量“history”,检查以前的变量,然后将其设置回该变量?
changeBack(){
// if last value was value2 change back to value2
// if last value was value3 then change back to value3
}
答:
let valueHistory = [];
function changeValue(newValue) {
valueHistory.push(this.value);
this.value = newValue;
}
function changeBack() {
if (valueHistory.length > 0) {
this.value = valueHistory.pop();
}
}
changeValue('hasanraza');
console.log(this.value);
评论
你的问题被标记为 vue.js,所以我在 vue 中的答案
你可以使用 vue 观察者并创建一个列表,并按它们的索引或对象选择它们,其中包含所有更改的键值对。
data() {
return {
val: '',
listOfValues: [],
}
},
watch: {
val(newVal, oldVal) {
this.listOfValues.push(newVal)
// or with if statement if you want to check
if (...) {
}
}
},
您可以使用数组来跟踪值的历史记录。
例:
class YourClass {
constructor() {
this.value = null;
this.valueHistory = [];
}
changeValue() {
if (!this.value) {
this.value = 'value1';
} else if (this.value === 'value1') {
this.value = 'value2';
} else if (this.value === 'value2') {
this.value = 'value3';
}
// Save the current value to history
this.valueHistory.push(this.value);
}
changeValues() {
if (this.value === 'value2' || this.value === 'value3') {
this.value = 'valueX';
}
}
changeBack() {
// Pop the last value from history and set it as the current value
if (this.valueHistory.length > 0) {
this.value = this.valueHistory.pop();
}
}
}
valueHistory
是一个跟踪值历史记录的数组。changeBack 方法从 history 数组中弹出最后一个值,并将其设置为当前值。
通用的、非侵入性的、因此可重用的方法需要基于 WeakMap
。此类注册表的键完全是对象的引用,其中后者的键希望存储每个对象条目的值历史记录。因此,注册表的每个值都需要是一个 Map
实例,其中基于映射字符串的键是对象条目的属性名称,值是 Set
实例。后者用作对象特定条目(键值对)的所有最近唯一值的历史记录存储。
然后,对象的任何属性都可以通过 get
/set
功能实现,该功能在要设置的值上会跟踪每个新的、唯一设置的值。
然后,代码必须附带两个辅助函数,例如 并且每个函数在传递的参数上,并且确实为特定于对象的 -indicate 属性返回传递参数之前或旁边的值。getPreviousOfValueHistory
getNextOfValueHistory
target
key
value
target
key
value
如果未显示在 / 指示属性的值历史记录中,则将返回符号值。value
target
key
Symbol('no history value found')
如果匹配第一个或最后一个唯一历史记录值,则返回该值本身。value
示例代码...
const noHistoryValueFound = Symbol('no history value found');
const valueHistoryRegistry = new WeakMap;
const getPreviousOfValueHistory = (target, key, value) => {
let result = noHistoryValueFound;
const valueHistory =
valueHistoryRegistry?.get(target)?.get(key);
if (valueHistory) {
const valueList = [...valueHistory];
let valueIdx = valueList.indexOf(value);
if (valueIdx >= 0) {
result = (--valueIdx in valueList)
? valueList.at(valueIdx)
: value;
}
}
return result;
}
const getNextOfValueHistory = (target, key, value) => {
let result = noHistoryValueFound;
const valueHistory =
valueHistoryRegistry?.get(target)?.get(key);
if (valueHistory) {
const valueList = [...valueHistory];
let valueIdx = valueList.indexOf(value);
if (valueIdx >= 0) {
result = (++valueIdx in valueList)
? valueList.at(valueIdx)
: value;
}
}
return result;
}
const logUniqueValueHistory = (target, key, value) => {
if (valueHistoryRegistry.has(target)) {
const valueRegistry = valueHistoryRegistry.get(target);
if (valueRegistry.has(key)) {
const valueHistory = valueRegistry.get(key);
if (!valueHistory.has(value)) {
valueHistory
.add(value);
}
} else {
valueRegistry
.set(key, new Set([value]));
}
} else {
valueHistoryRegistry
.set(target, new Map([[key, new Set([value])]]));
}
}
const getSnapshotOfWritableOwnProperties = (value) => {
const source = Object(value);
return Object
.entries(
Object.getOwnPropertyDescriptors(source)
)
.concat(
Object
.getOwnPropertySymbols(source)
.map(symbol => [symbol, Object.getOwnPropertyDescriptor(source, symbol)])
)
.filter(([_, descriptor]) =>
!!descriptor.writable && Object.hasOwn(descriptor, 'value')
)
.reduce((snapshot, [key, { value }]) => {
snapshot[key] = value;
return snapshot;
}, {});
}
function withContextuallyBoundUniqueValueHistory(key, value) {
const { context, state } = this;
Reflect.defineProperty(context, key, {
get: () => Reflect.get(state, key),
set: value => {
logUniqueValueHistory(context, key, value);
return Reflect.set(state, key, value);
},
});
}
function withOwnUniqueValueHistory() {
const state = getSnapshotOfWritableOwnProperties(this);
Object
.entries(state)
.concat(
Object
.getOwnPropertySymbols(state)
.map(symbol => [symbol, Reflect.get(state, symbol)])
)
.forEach(([key, value]) => {
withContextuallyBoundUniqueValueHistory
.call({ context: this, state }, key, value);
// - force 1st history-value entry
// for every instance property.
this[key] = value;
});
}
class KeyValueStorageWithUniqueValueHistory {
constructor(initialState= {}) {
Object
.assign(this, Object(initialState));
withOwnUniqueValueHistory.call(this);
}
previousValueOf(/* key, value? */...args) {
const key = args.at(0);
const value = ('1' in args) ? args.at(1) : this[key];
return getPreviousOfValueHistory(this, key, value);
}
nextValueOf(/* key, value? */...args) {
const key = args.at(0);
const value = ('1' in args) ? args.at(1) : this[key];
return getNextOfValueHistory(this, key, value);
}
}
const fooBarSymbol = Symbol('fooBar');
const storage = new KeyValueStorageWithUniqueValueHistory({
quickBrownFox: 'the',
[fooBarSymbol]: 'foo',
});
console.log(`const storage = new KeyValueStorageWithUniqueValueHistory({
quickBrownFox: 'the',
[fooBarSymbol]: 'foo',
});`);
console.log('storage.quickBrownFox ...', storage.quickBrownFox);
console.log('\n');
console.log("storage.quickBrownFox = 'quick';");
storage.quickBrownFox = 'quick';
console.log('storage.quickBrownFox ...', storage.quickBrownFox);
console.log('\n');
console.log("storage.quickBrownFox = 'brown';");
storage.quickBrownFox = 'brown';
console.log('storage.quickBrownFox ...', storage.quickBrownFox);
console.log('\n');
console.log("storage.quickBrownFox = 'fox';");
storage.quickBrownFox = 'fox';
console.log('storage.quickBrownFox ...', storage.quickBrownFox);
console.log('\n');
console.log("storage.quickBrownFox = 'jumps';");
storage.quickBrownFox = 'jumps';
console.log('storage.quickBrownFox ...', storage.quickBrownFox);
console.log('----- ----- ----- ----- -----');
console.log("storage.quickBrownFox = 'brown';");
storage.quickBrownFox = 'brown';
console.log('storage.quickBrownFox ...', storage.quickBrownFox);
console.log('\n');
console.log(
"storage.previousValueOf('quickBrownFox') ...",
storage.previousValueOf('quickBrownFox'),
);
console.log(
"storage.nextValueOf('quickBrownFox') ...",
storage.nextValueOf('quickBrownFox'),
);
console.log('\n');
console.log(
"storage.previousValueOf('quickBrownFox', 'quick') ...",
storage.previousValueOf('quickBrownFox', 'quick'),
);
console.log(
"storage.previousValueOf('quickBrownFox', 'jumps') ...",
storage.previousValueOf('quickBrownFox', 'jumps'),
);
console.log('\n');
console.log(
"storage.nextValueOf('quickBrownFox', 'the') ...",
storage.nextValueOf('quickBrownFox', 'the'),
);
console.log(
"storage.nextValueOf('quickBrownFox', 'fox') ...",
storage.nextValueOf('quickBrownFox', 'fox'),
);
console.log('----- ----- ----- ----- -----');
console.log('storage[fooBarSymbol] ...',storage[fooBarSymbol]);
console.log(
"storage.previousValueOf(fooBarSymbol) ...",
storage.previousValueOf(fooBarSymbol),
);
console.log(
"storage.nextValueOf(fooBarSymbol) ...",
storage.nextValueOf(fooBarSymbol),
);
console.log('\n');
console.log(
"storage.nextValueOf(fooBarSymbol, 'bar') ...",
String(storage.nextValueOf(fooBarSymbol, 'bar')),
);
console.log('----- ----- ----- ----- -----');
console.log("storage[fooBarSymbol] = 'bar';");
storage[fooBarSymbol] = 'bar';
console.log('storage[fooBarSymbol] ...', storage[fooBarSymbol]);
console.log('\n');
console.log("storage[fooBarSymbol] = 'baz';");
storage[fooBarSymbol] = 'baz';
console.log('storage[fooBarSymbol] ...', storage[fooBarSymbol]);
console.log('\n');
console.log("storage[fooBarSymbol] = 'biz';");
storage[fooBarSymbol] = 'biz';
console.log('storage[fooBarSymbol] ...', storage[fooBarSymbol]);
console.log('----- ----- ----- ----- -----');
console.log("storage[fooBarSymbol] = 'bar';");
storage[fooBarSymbol] = 'bar';
console.log('storage[fooBarSymbol] ...', storage[fooBarSymbol]);
console.log('\n');
console.log(
"storage.previousValueOf(fooBarSymbol) ...",
storage.previousValueOf(fooBarSymbol),
);
console.log(
"storage.nextValueOf(fooBarSymbol) ...",
storage.nextValueOf(fooBarSymbol),
);
console.log('\n');
console.log(
"storage.previousValueOf(fooBarSymbol, 'biz') ...",
storage.previousValueOf(fooBarSymbol, 'biz'),
);
console.log(
"storage.nextValueOf(fooBarSymbol, 'baz') ...",
storage.nextValueOf(fooBarSymbol, 'baz'),
);
.as-console-wrapper { min-height: 100%!important; top: 0; }
考虑到上述示例代码提供的所有内容,OP 现在可以轻松实现如下所示的通用方法......changeBack
setPreviousValueOf(/* key, value? */...args) {
const key = args.at(0);
const value = ('1' in args) ? args.at(1) : this[key];
this[key] = getPreviousOfValueHistory(this, key, value);
}
...然后可以像......
this.setPreviousValueOf('valueX'/*,anyCurrentOrPreviousValueOfProperty_valueX*/);
同样,堆栈代码段的示例代码确实提供了其用法的实现和示例。getPreviousOfValueHistory
评论
prevValue