8

我想Proxy在一个名为的自定义类上使用,ObservableList其中包含一个Array. 由于Proxy仅在 ES6 之后可用,我想知道是否有任何替代实现。

我的要求是一旦发生变化就让观察者得到更新(而不是被注意到)ObservableList,这样观察者总是与带有某种过滤或映射方法的可观察者保持一致。

var activities = new ObservableList(['reading', 'swimming']);
var sAct = activities.filter(function(v) {
  return v[0] === 's';
});
// expect sAct.list to be ['swimming']
var meAct = activities.map(function(v) {
  return 'I am ' + v;
});
// expect meAct.list to be ['I am reading', 'I am swimming']

activities.list.push('smiling');
console.log(sAct.list, meAct.list);
// expect sAct.list to be ['swimming', 'smiling']
// expect meAct.list to be ['I am reading', 'I am swimming', 'I am smiling']

activities.list[1] = 'snoopying';
console.log(sAct.list, meAct.list);
// expect sAct.list to be ['swimming', 'snoopying']
// expect meAct.list to be ['I am reading', 'I am snoopying', 'I am smiling']

我的代理实现可在https://jsfiddle.net/ovilia/tLmbptr0/3/

4

3 回答 3

2

用过defineProperty

不完全符合您的要求。我刚刚实现了一个“反应阵列”。但我认为它可能适用于您的问题。

坏的部分:

  1. 在目标上定义了大量的 getter/setter。
  2. 访问未定义的索引器不会是被动的。
  3. update()有待优化。

好的部分:

  1. ES5 友好。
  2. 如果不需要索引器,则使用set(i, val)/get(i)将是被动的。

https://jsfiddle.net/jimnox/jrtq40p7/2/

于 2016-06-03T19:00:05.223 回答
0

使用代理是硬性要求吗?我不建议将代理用于一般编程任务,因为最终可能会产生不可预测且难以发现的副作用。

如果你坚持使用数据和函数来转换它,尽可能避免可变状态,我认为你最终会得到更容易维护的更简单的代码。

var activities = ['reading', 'swimming'];

var sfilter = function(activities){
    return activities.filter(function(v){
        return v[0] === 's';
    });
};

console.log(sfilter(activities));

var memap = function(activities){
    return activities.map(function(v){
        return 'I am ' + v;
    });
};

console.log(memap(activities));

activities.push('smiling');
console.log(sfilter(activities));
console.log(memap(activities));

// Yes, I know this doesn't work in quite the same way,
// but you're asking for trouble here since in your
// code you're appending to one list, but overwriting
// an element in the other.
activities[1] = 'snoopying';
console.log(sfilter(activities));
console.log(memap(activities));

坚持单一的真理来源并观察它。对于每个副本,您都在增加状态复杂性。这将使调试、测试和扩展代码变得困难。

于 2016-06-07T21:22:40.663 回答
0

如问题中所述,我只需要ObservableList包含一个Array而不是扩展它,就像 Jim 在他的复杂答案中所做的那样。令人惊讶的是,我发现这可以通过包装原始Array操作轻松实现。

一个限制是索引操作在我的实现中不是被动的,因为我未能找到捕获索引操作的正确方法。如果你有更好的想法,欢迎告诉我!XD

这是完整的实现。

export class ObservableList {

  list: Array<any>;

  private _observer: Array<ObserverList>;

  constructor(list?: Array<any>) {
    this.list = list || [];
    this._initList();
    this._initMethods();

    this._observer = [];
  }

  notify(): void {
    for (let o of this._observer) {
      o.update();
    }
  }

  private _initList(): void {
    var that = this;
    var operations = ['push', 'pop', 'shift', 'unshift', 'splice',
      'sort', 'reverse'];
    for (let operation of operations) {
      this.list[operation] = function() {
        var res = Array.prototype[operation].apply(that.list, arguments);
        that.notify();
        return res;
      }
    }
  }

  private _initMethods(): void {
    var that = this;
    var methods = ['filter', 'map'];
    for (let method of methods) {
      this[method] = (formatter: Function): ObserverList => {
        var observer = new ObserverList(that, formatter, method);
        this._observer.push(observer);
        return observer;
      }
    }
  }

}

export class ObserverList {

  public list: Array<any>;

  constructor(public observable: ObservableList, 
              public formatter: Function, 
              public method: string) {
    this.list = [];
    this.update();
  }

  update(): void {
    var list = [];
    var master = this.observable.list;
    for (var i = 0, len = master.length; i < len; ++i) {
      if (this.method === 'filter') {
        if (this.formatter(master[i])) {
          list.push(master[i]);
        }
      } else if (this.method === 'map') {
        list.push(this.formatter(master[i]));
      } else {
        console.error('Illegal method ' + this.method + '.');
      }
    }
    this.list = list;
  }

}
于 2016-06-08T04:31:28.153 回答