JavaScript中的自定义类数组getter

Custom Array-like getter in JavaScript

本文关键字:数组 getter 自定义 JavaScript      更新时间:2023-09-26

我有一个简单的ES6类,像这样:

class Ring extends Array {
    insert (item, index) {
        this.splice(index, 0, item);
        return this;
    }
}

我想让Ring对象的索引包装,这样new Ring(1, 2, 3)[3]返回1,new Ring(1, 2, 3)[-1]返回3,等等。这在ES6中可能吗?如果是,我该如何实现它?

我读过代理,它允许完全自定义getter,但我不知道如何将代理应用于类。我确实做到了:

var myRing = new Proxy (Ring.prototype, {
    get: function (target, name) {
        var len = target.length;
        if (/^-?'d+$/.test(name))
            return target[(name % len + len) % len];
        return target[name];
    }
});

myRing现在是一个环对象,支持包裹索引。问题是我每次都必须这样定义Ring对象。是否有一种方法将此代理应用于类,以便调用new Ring()返回它?

基本上是

class ProxyRing extends Array {
  constructor(...args) {
    super(...args)
    return new Proxy(this, {
      get: function (target, name) {
          var len = target.length;
          if (typeof name === 'string' && /^-?'d+$/.test(name))
              return target[(name % len + len) % len];
          return target[name];
      }
    });
  }
  insert (item, index) {
      this.splice(index, 0, item);
      return this;
  }
}

警告:这是一个丑陋的hack

仔细想想,这是一个相当简单的方法。

function ClassToProxy(_class, handler) {
    return (...args) => new Proxy(new _class(...args), handler);
}

定义了一个函数 ClassToProxy 。第一个参数是要添加行为的类,第二个参数是处理程序。


下面是用法示例:

const Ring = ClassToProxy(
    // Class
    class Ring {
        constructor(...items) {
            this.items = items;
        }
    },
    // Handler
    {
        get: function(target, name) {
            return target.items[name];
        }
    }
)

你基本上有两个选择:

  • 在每个实例周围包装一个Proxy

    const handler = {
        get(target, name) {
            var len = target.length;
            if (typeof name === 'string' && /^-?'d+$/.test(name))
                return target[(name % len + len) % len];
            return target[name];
        }
    };
    class Ring extends Array {
        constructor() {
            super()
            return new Proxy(this, handler);
        }
        …
    }
    
  • 在类的原型周围包裹一个Proxy

    class Ring extends Array {
        constructor() {
            super()
        }
        …
    }
    Ring.prototype = new Proxy(Ring.prototype, {
        get(target, name, receiver) {
            var len = target.length;
            if (typeof name === 'string' && /^-?'d+$/.test(name)) {
                if (+name < 0)
                    return receiver[(name % len) + len];
                if (+name > len-1)
                    return receiver[name % len];
            }
            return target[name];
        }
    });