Настраиваемый массивоподобный геттер в JavaScript

#javascript #ecmascript-6 #es6-class #es6-proxy

#javascript #ecmascript-6 #es6-класс #es6-прокси

Вопрос:

У меня есть простой класс ES6, например:

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

Я хочу сделать так, чтобы индексация для кольцевых объектов переносилась, так что new Ring(1, 2, 3)[3] возвращает 1, new Ring(1, 2, 3)[-1] возвращает 3 и так далее. Возможно ли это в ES6? Если да, то как мне это реализовать?

Я читал о прокси, которые позволяют полностью настроить геттер, но я не могу понять, как применить прокси к классу. Я справился с этим:

 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 теперь это кольцевой объект, который поддерживает перенос индексов. Проблема в том, что мне пришлось бы определять кольцевые объекты подобным образом каждый раз. Есть ли способ применить этот прокси к классу так, чтобы вызов new Ring() возвращал его?

Комментарии:

1. Просто оберните new Proxy (...) с помощью функции конструктора и вызовите ее с new помощью . Да, вы не можете сделать это без прокси.

Ответ №1:

В основном это

 class ProxyRing extends Array {
  constructor(...args) {
    super(...args)

    return new Proxy(this, {
      get: function (target, name) {
          var len = target.length;
          if (typeof name === 'string' amp;amp; /^-?d $/.test(name))
              return target[(name % len   len) % len];
          return target[name];
      }
    });
  }

  insert (item, index) {
      this.splice(index, 0, item);
      return this;
  }
}
  

Комментарии:

1. Ну … кажется, это работает отлично! Я не понимал, что функция конструктора может явно возвращать значение…

2. Ага… Вау, я никогда не знал, что вы можете возвращать значение из конструктора в классах ES6…

3. Да, это так . В большинстве случаев это было бы неоправданно, но это именно тот случай.

Ответ №2:

Предупреждение: это уродливый взлом

Это довольно простой подход, если подумать.

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

Комментарии:

1. Хм… Это довольно близко, за исключением того, что он не поддерживает new .

Ответ №3:

В основном у вас есть два варианта:

  • Оберните a Proxy вокруг каждого экземпляра

     const handler = {
        get(target, name) {
            var len = target.length;
            if (typeof name === 'string' amp;amp; /^-?d $/.test(name))
                return target[(name % len   len) % len];
            return target[name];
        }
    };
    class Ring extends Array {
        constructor() {
            super()
            return new Proxy(this, handler);
        }
        …
    }
      
  • оберните a 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' amp;amp; /^-?d $/.test(name)) {
                if ( name < 0)
                    return receiver[(name % len)   len];
                if ( name > len-1)
                    return receiver[name % len];
            }
            return target[name];
        }
    });
      

Комментарии:

1. Второй метод, похоже, не работает для меня. Я попытался использовать только прокси-часть для расширения класса Number, и мне удалось заставить это работать, изменив оба экземпляра Number.prototype на Number.prototype.__proto__ .

2. @ETHproductions Не уверен, зачем вам создавать Number.prototype прокси? И вам, вероятно, следует изменить a .__proto__ , поскольку это вводит другой объект в цепочку прототипов.