Преимущества использования композиции/делегирования по сравнению с наследованием в JavaScript и других языках хорошо задокументированы:

Замените наследование делегированием

Композиция над наследством

Одна из главных причин, по которой многие разработчики избегают написания кода на основе делегирования, заключается в том, что это замедляет разработку из-за необходимости писать кучу шаблонов для пересылки вызовов функций делегату. Прокси могут значительно сократить объем необходимого кода. Что, если бы вы могли написать только это:

class MyClass {
  constructor(delegate) {
    this.myDelegate = delegate;
    return delegate(this,"myDelegate");
  })
  myClassMethod() { ... }
  methodThatHidesADelgateMethod() { ... }
  methodThanEnhancesADelegateMethod(arg) {
    const modifiedArg = arg++;
    return this.myDelegate(modifiedArg);
  }
  // all other method calls just forward to the delegate!
}

Во-первых, давайте для ясности напишем код непосредственно в контексте определения класса:

class MyClass {
  constructor(delegate) {
   this.myDelegate = delegate;
   return new Proxy(this,{
     get: (target,property) => {
       // return value if property is in target instance
       if(property in target) return target[property];
       // selectively block forwarding
       if(block.includes[property]) return;
       // get the property from the delegate
       const value = target[delegateProperty][property];
       if(typeof(value)==="function") {
         // if it is a function, proxy it so that scope is correct
         return new Proxy(value,{
            apply: (f,thisArg,argumentsList) => {
              // if trying to call on target, then use delegate
              // else call on provided thisArg
              const scope = (thisArg===target
                               ? target.myDelegate
                               : thisArg);
              return f.apply(scope,argumentsList);
             }
         });
       }
       return value;
    }
  })
  myClassMethod() { ... }
  methodThatHidesADelgateMethod() { ... }
  methodThanEnhancesADelegateMethod(arg) {
    const modifiedArg = arg++;
    return this.delegate(modifiedArg);
  }
  // all other method calls just forward to the delegate!
}

Теперь сделайте настройку прокси многоразовой, инкапсулировав ее в функцию:

// object is the instance from which to delegate
// delegateProperty is the property containing the delegate
// block is an Array of properties to hide on the delegate
const delegate =(object,delegateProperty,block=[]) => {
  return new Proxy(object,{
     get: (target,property) => {
       // return value if property is in target instance
       if(property in target) return target[property];
       // selectively block forwarding
       if(block.includes[property]) return;
       // get the property from the delegate
       const value = target[delegateProperty][property];
       if(typeof(value)==="function") {
         // if it is a function, proxy it so that scope is correct
         return new Proxy(value,{
            apply: (f,thisArg,argumentsList) => {
              // if trying to call on target, then use delegate
              // else call on provided thisArg
              const scope = (thisArg===target
                               ? target[delegateProperty]
                               : thisArg);
              return f.apply(scope,argumentsList);
             }
         });
       }
       return value;
    }
  })
}

Итак, теперь мы можем написать только это:

class MyClass {
  constructor(delegate) {
    this.delegate = delegate;
    return delegate(this,"delegate");
  })
  myClassMethod() { ... }
  methodThatHidesADelgateMethod() { ... }
  methodThanEnhancesADelegateMethod(arg) {
    const modifiedArg = arg++;
    return this.delegate(modifiedArg);
  }
  // all other method calls just forward to the delegate!
}

Конкретный пример использования этого метода можно увидеть в CacheStore, оболочке кэширования, которая предоставляет localStore API для различных механизмов хранения ключей/значений.