有没有办法跟踪对象属性的值历史,例如记住和回忆其最近的值?

问题描述 投票:0回答:4

假设我有一个方法可以根据当前值更改值。我在这里就让它变得非常简单:

  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。

有没有办法查看变量“历史记录”,检查以前的内容,然后将其设置回原来的状态?

  changeBack(){
    // if last value was value2 change back to value2
    // if last value was value3 then change back to value3
  }
javascript arrays storage getter-setter key-value-store
4个回答
1
投票

您可以使用数组来跟踪值的历史记录。

示例:

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 方法从历史数组中弹出最后一个值并将其设置为当前值。


0
投票
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);

0
投票

你的问题被标记为 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 (...) {
    
    }
  }
},

Vue 观察者


0
投票

通用的、非侵入性的、可重复使用的方法需要基于

WeakMap
。这样的注册表的键专门是对象的引用,其中后者想要存储每个对象条目的值历史记录。因此,注册表的每个值都需要是一个
Map
实例,其中基于映射字符串的键是对象条目的属性名称,值是
Set
实例。后者用作对象的某个条目(键值对)的所有最近唯一值的历史存储。

对象的任何属性都可以通过

get
/
set
功能来实现,该功能根据要设置的值确实跟踪每个新的、唯一设置的值。

代码必须附带两个辅助函数,例如

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 withContextuallyBoundUniqueValueHistoryLogging(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]) => {

      withContextuallyBoundUniqueValueHistoryLogging
        .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(...args) {
    const key = args.at(0);
    const value = ('1' in args) ? args.at(1) : this[key];

    return getPreviousOfValueHistory(this, key, value);
  }
  nextValueOf(...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',
  fooBarBazBiz: '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 现在可以轻松地将具有挑战性的

changeValues
函数从 ...

changeValues () {
  if (this.value === 'value2' || this.value === 'value3') {
    this.value = 'valueX';
  }
}

...到...

changeBack () {
  if (this.value === 'value2' || this.value === 'value3') {
    this.value
      = getPreviousOfValueHistory(this, 'value', this.value);
  }
}

同样,堆栈片段的示例代码确实提供了

getPreviousOfValueHistory
的实现及其用法示例。

© www.soinside.com 2019 - 2024. All rights reserved.