【问题标题】:Javascript observer or proxy without all changes going through proxyJavascript 观察者或代理,所有更改都通过代理
【发布时间】:2019-06-10 02:20:30
【问题描述】:

我正在用 Javascript 编写数组的子类,以便更好地支持矩阵运算(我知道还有其他的,这部分是为了让我重新自学线性代数),我想要的是有一些属性每当调整矩阵中的任何值时都会重置。像行列式这样的一些计算是计算密集型的,我希望能够存储它们以避免重新计算,但是每当任何矩阵元素发生变化时,它们都需要重置为 null。

基本上,我想要的是已弃用的Array.observe()。而替换,代理,对于这件事来说似乎是很多开销。正如Detecting Changes in a Javascript Array using the proxy object 上的一些未直接解决的cmets 中所提到的,我不想只通过代理访问我的矩阵。到目前为止,我在编写的代码中使用了很多方便的[i][j] 索引和[mat[i], mat[j]] = [mat[j], mat[i]]

class Matrix extends Array {
  constructor() {
    var args  = [];
    for (var i = 0; i < arguments.length; i++) {
        if (Array.isArray(arguments[i])) {
        args.push(new Matrix(...arguments[i]));
      } else {
        args.push(arguments[i]);
      }
    }
    super(...args);
    this._determinant = null;
  }
  determ(forceRecalculate = false) {
    if (this._determinant === null || forceRecalculate) {
        this.upperEchelon();
    }
    return this._determinant;
  }
  upperEchelon(reduced = false) {
  //There's a lot of code here but in the process of doing this other thing
  //you get 99% of the way to calculating the determinant so it does this
  this._determinant = factor;
  }
}

基本上,我想要像 mat[0][0] = 10mat.push([2,4,5]) 这样更新矩阵中的值以设置 mat._determinant = null。或任何等效的标记方法,需要在下次请求时重新计算。如果有人可以帮助我弄清楚实现,我并不一定反对使用代理,我只是希望这个 set-to-null-on-update 属性是我的类功能所固有的。

我真正想要的是一种重载基本方法的方法,例如 [] 一个 la C#,所以执行更新的函数会在不改变语法的情况下触发它,但我已经让自己在 JS 中没有这种方法。

【问题讨论】:

  • 你的 Matrix 的其他方法是否需要使用 _determinant,还是只有在调用determ 时才会使用(返回)?
  • 不要使用Proxy 拦截属性访问...生成的Matrix 实例会非常慢。
  • @CertainPerformance 有许多方法需要_determinantdeterm() 的返回值(我真的不在乎哪一个,我知道我可以将determ() 替换为一个适当的吸气剂)。许多方法通常都需要它,如果可能的话,我希望不必重新计算它。
  • 谢谢,也是通过多种方法设置,还是只通过upperEchelon? (例如,每当您需要一个新的行列式时,您是否可以调用 upperEchelon(或其他方法)来根据需要设置新值,但操作代价高昂?)
  • upperEchelon 是计算它的最快方法,因此它应该是唯一需要设置它的方法。 determ() 有一点 === null 逻辑的原因是希望我可以在更新时将其设置为 null 并保存重新计算直到实际需要该值。

标签: javascript arrays observers proxy-pattern


【解决方案1】:

虽然代理可以工作,但它也会很慢。对于需要使用 _determinant 值的每个方法,另一种方法是首先通过 不同 函数检查是否需要更新 _determinant(如果需要,更新它)。这样,每次数组更改时都不会进行昂贵的重新计算,而只是及时使用结果。例如:

class Matrix extends Array {
  constructor() {
    var args  = [];
    for (var i = 0; i < arguments.length; i++) {
      if (Array.isArray(arguments[i])) {
        args.push(new Matrix(...arguments[i]));
      } else {
        args.push(arguments[i]);
      }
    }
    super(...args);
    this._determinant = null;
  }
  // next method is effectively a recursive deep join
  // could also use toString if it doesn't interfere with anything else
  getString() {
    const itemsStr = this.map((item) => (
    item instanceof Matrix
      ? item.getString()
      : item
    ))
    .join(',');
    const result = '[' + itemsStr + ']';
    return result;
  }
  getDeterm() {
    const newString = this.getString();
    if (newString !== this._lastString) {
      this._lastString = newString;
      this.upperEchelon();
    }
    return this._determinant;
  }
  upperEchelon() {
    console.log('running upperEchelon');
    this._determinant = Math.random();
  }
}

const m = new Matrix([2, 3, 4], 5);
console.log(m.getDeterm());
// Not calculated again:
console.log(m.getDeterm());
// Mutation, next call of getDeterm will run upperEchelon:
m[0][0] = 1;
console.log(m.getDeterm());

【讨论】:

  • 这很聪明!它就像一个魅力。非常感谢!
猜你喜欢
  • 2019-09-19
  • 1970-01-01
  • 1970-01-01
  • 2019-06-25
  • 1970-01-01
  • 1970-01-01
  • 2020-05-22
  • 1970-01-01
  • 2019-12-12
相关资源
最近更新 更多