bind.js

/**
 * Bind decorator.
 * @function bind
 *
 * @license {@link https://opensource.org/licenses/MIT|MIT}
 *
 * @author Patrick Heng <hengpatrick.pro@gmail.com>
 * @author Fabien Motte <contact@fabienmotte.com>
 *
 * @param {Object} target Target.
 * @param {string} key Target key.
 * @param {Object} descriptor Descriptor for the target being modified.
 * @param {function} descriptor.value Value.
 * @param {function} descriptor.configurable Configurable.
 * @param {function} descriptor.enumerable Enumerable.
 *
 * @throws {SyntaxError} Bind decorator can only be applied to a method.
 *
 * @returns {function} Bound function.
 */
export default function bind (target, key, descriptor) {
  if (typeof descriptor === 'undefined' || typeof descriptor.value !== 'function') {
    throw new SyntaxError(`@bind decorator can only be applied to a method`)
  }

  const { value: fn, configurable, enumerable } = descriptor

  return {
    configurable,
    enumerable,
    get () {
      // Property prototype direct access
      if (this === target) {
        return fn
      }

      const boundFn = fn.bind(this)

      Object.defineProperty(this, key, {
        configurable: true,
        writable: true,
        enumerable: false,
        value: boundFn
      })

      return boundFn
    }
  }
}