admin管理员组

文章数量:1290230

I have a simple ES6 class, like so:

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

I want to make it so that the indexing for Ring objects wraps, so that new Ring(1, 2, 3)[3] returns 1, new Ring(1, 2, 3)[-1] returns 3, and so on. Is this possible in ES6? If so, how would I implement it?

I've read about proxies, which allow a pletely customized getter, but I can't figure out how to apply a proxy to a class. I did manage this:

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 is now a Ring object that supports wrapping indices. The problem is that I'd have to define Ring objects like that every time. Is there a way to apply this proxy to the class such that calling new Ring() returns it?

I have a simple ES6 class, like so:

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

I want to make it so that the indexing for Ring objects wraps, so that new Ring(1, 2, 3)[3] returns 1, new Ring(1, 2, 3)[-1] returns 3, and so on. Is this possible in ES6? If so, how would I implement it?

I've read about proxies, which allow a pletely customized getter, but I can't figure out how to apply a proxy to a class. I did manage this:

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 is now a Ring object that supports wrapping indices. The problem is that I'd have to define Ring objects like that every time. Is there a way to apply this proxy to the class such that calling new Ring() returns it?

Share Improve this question asked Oct 1, 2016 at 2:19 ETHproductionsETHproductions 4995 silver badges19 bronze badges 1
  • 1 Just wrap new Proxy (...) with constructor function and call it with new. Yes, you can't do that without proxy. – Estus Flask Commented Oct 1, 2016 at 2:35
Add a ment  | 

3 Answers 3

Reset to default 7

Basically it is

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;
  }
}

Warning: This is an ugly hack

This is a rather simple approach when you think about it.

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

This defined a function ClassToProxy. The first argument is the class you want to add behavior too, and the second is the handler.


Here's example usage:

const Ring = ClassToProxy(

    // Class
    class Ring {
        constructor(...items) {
            this.items = items;
        }
    },

    // Handler
    {
        get: function(target, name) {
            return target.items[name];
        }
    }
)

You basically have two choices:

  • Wrap a Proxy around each instance

    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);
        }
        …
    }
    
  • wrap a Proxy around the prototype of your class

    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];
        }
    });
    

本文标签: ecmascript 6Custom Arraylike getter in JavaScriptStack Overflow