2016-10-01 13 views
8

Mam proste klasy ES6, tak:klienta Array podobny getter w JavaScript

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

chcę to zrobić tak, że indeksowanie dla pierścienia obiektów okłady, tak że new Ring(1, 2, 3)[3] zwraca 1, new Ring(1, 2, 3)[-1] zwraca 3 , i tak dalej. Czy to możliwe w ES6? Jeśli tak, w jaki sposób mogę go wdrożyć?

Przeczytałem o serwerach proxy, które pozwalają na całkowicie dostosowany program pobierający, ale nie wiem, jak zastosować proxy do klasy. I udało to:

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 jest teraz obiektem Pierścień, który obsługuje zawijania indeksów. Problem polega na tym, że za każdym razem musiałbym definiować takie obiekty Ring. Czy istnieje sposób zastosowania tego proxy do klasy tak, aby wywołanie go zwróciło?

+0

Wystarczy owinąć 'nowy Proxy (...)' z funkcji konstruktora i nazywają go 'new'. Tak, nie możesz tego zrobić bez proxy. – estus

Odpowiedz

5

Zasadniczo jest

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

Cóż ... to wydaje się działać idealnie! Nie zdawałem sobie sprawy, że funkcja konstruktora może jawnie zwrócić wartość ... – ETHproductions

+0

Huh ... Wow, nigdy nie wiedziałem, że możesz zwrócić wartość konstruktora z klas ES6 ... – Downgoat

+0

Tak, [oni są] (https : //developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/new#Description). W większości przypadków byłoby to nieuzasadnione, ale tak właśnie się dzieje. – estus

3

Ostrzeżenie: Jest brzydka Hack

Jest to raczej proste podejście, gdy myślisz o tym.

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

to zdefiniowano funkcję ClassToProxy. Pierwszym argumentem jest klasa, do której chcesz dodać zachowanie, a druga to obsługa.


Oto przykład użycia:

const Ring = ClassToProxy(

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

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

Hmm ... To całkiem blisko, tyle że nie obsługuje 'nowego'. – ETHproductions

+0

To nie działa z 'new' lub' instanceof' – Bergi

0

Zasadniczo są dwie opcje:

  • owinąć Proxy wokół każdej instancji

    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); 
        } 
        … 
    } 
    
  • owinąć Proxy wokół prototypu klasy

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

Druga metoda nie działa dla mnie. Próbowałem używać tylko części Proxy, aby rozszerzyć klasę Number, i udało mi się ją uruchomić, zmieniając oba wystąpienia 'Numer.prototype' na' Numer.prototype .__ proto__'. – ETHproductions

+0

@ETHproductions Nie masz pewności, dlaczego stworzysz 'Numer.prototype' jako serwer proxy? Prawdopodobnie powinieneś zmienić '.__ proto__', ponieważ wprowadza on inny obiekt w łańcuchu prototypów. – Bergi