Home > Net >  Within a class-free, purely function-based, extending type system how does one emulate the invocatio
Within a class-free, purely function-based, extending type system how does one emulate the invocatio

Time:12-06

I wish to call the parent function (without super) instead of the inherited child function. What options do I have without using ES classes?

    function Queue() {
      this.items = [];
      this.enqueue = function enqueue(item) {
        this.items.push(item);
        return item;
      }
    }
    
    function AsyncQueue() {
      Queue.call(this);
      this.awaiters = new Queue();
    
      this.enqueue = function enqueue(item) {
        const awaiter = this.awaiters.dequeue();
        if (awaiter !== undefined) {
          setImmediate(() => {
            awaiter(item);
          });
        } else {
          super.enqueue(item);
        }
        return item;
      }
    }

AsyncQueue.prototype = Object.create(Queue.prototype);
AsyncQueue.prototype.constructor = AsyncQueue;

CodePudding user response:

Put your methods on the prototype where they belong. Then you can directly call the parent method:

function Queue() {
  this.items = [];
}
Queue.prototype.enqueue = function enqueue(item) {
  this.items.push(item);
  return item;
};

function AsyncQueue() {
  Queue.call(this);
  this.awaiters = new Queue();
}

AsyncQueue.prototype = Object.create(Queue.prototype);
AsyncQueue.prototype.constructor = AsyncQueue;
AsyncQueue.prototype.enqueue = function enqueue(item) {
  const awaiter = this.awaiters.dequeue();
  if (awaiter !== undefined) {
    setImmediate(() => {
      awaiter(item);
    });
  } else {
    Queue.prototype.enqueue.call(this, item);
  }
  return item;
};

CodePudding user response:

The OP after already having mastered the Queue super-call within the AsyncQueue subtype constructor ...

Queue.call(this);

... needs to save the later to be used super.enqueue reference by assigning a bound version of it before reassigning / overwriting / shadowing it with the subtype's own enqueue implementation ...

const superEnqueue = this.enqueue.bind(this);

this.enqueue = function enqueue (item) {
  // ...
}

The next provided example code does this in addition to some other small suggested improvements ...

function Queue() {
  // kind of "private class field" assured by local scope.
  const items = [];

  // privileged (thus non prototypal) methods
  // with access capability via local scope.
  this.dequeue = function dequeue (item) {
    return items.shift(item);
  }
  this.enqueue = function enqueue (item) {
    items.push(item);
    return item;
  }
}

function AsyncQueue() {
  // super call.
  Queue.call(this);

  // save/keep the initial `super.enqueue` reference by binding it.
  const superEnqueue = this.enqueue.bind(this);

  this.awaiters = new Queue; 

  // overwrite/shadow the initial `super.enqueue` reference.
  this.enqueue = function enqueue (item) {

    const awaiter = this.awaiters.dequeue();
    // if (awaiter !== undefined) {

    // - one does want to know whether
    //   `awaiter` is a function since
    //   it is going to be invoked.
    if ('function' === typeof awaiter) {

      // setImmediate(() => awaiter(item));

      // - one does want to use a somewhat
      //   similar but standardized way.
      setTimeout(awaiter, 0, item);
    } else {
      // super.enqueue(item);

      // forwarding by using the above's hand-nitted super-delegation.
      superEnqueue(item);
    }
    return item;
  }
}
AsyncQueue.prototype = Object.create(Queue.prototype);
AsyncQueue.prototype.constructor = AsyncQueue;

const asyncQueue = new AsyncQueue;

console.log(
  '  enqueue ...',
  asyncQueue.enqueue('the')   // the
);
console.log(
  '  enqueue ...',
  asyncQueue.enqueue('quick') // quick
);
console.log(
  '  enqueue ...',
  asyncQueue.enqueue('brown') // brown
);
console.log(
  '  enqueue ...',
  asyncQueue.enqueue('fox')   // fox
);
console.log(
  '- dequeue ...',
  asyncQueue.dequeue()        // the
);
console.log(
  '  enqueue ...',
  asyncQueue.enqueue('jumps') // jumps
);
console.log(
  '- dequeue ...',
  asyncQueue.dequeue()        // quick
);

console.log(
  '    awaiters enqueue ...',
  asyncQueue.awaiters .enqueue(
    (...args) => console.log({ args })        
  )                           // (...args) => console.log({ args })
);
console.log(
  '  enqueue ...',
  asyncQueue.enqueue('over')  // over
);
console.log(
  '- dequeue ...',
  asyncQueue.dequeue()        // brown
);
// async, timeout based, logging { "args": ["over"] }
.as-console-wrapper { min-height: 100%!important; top: 0; }

Comparing the above implementation and the next provided one which uses class syntax and considering some comments from above ...

"why don't you want to use classes, this is what they were made for" – Xiduzo Nov 27 at 16:00

"Avoiding it that is all. bad me. but i definitely need help here." – Gary Nov 27 at 16:46"

... the questions remains ... "What is the reason for the OP being willing to sacrifice the much more convenient and safer way of subtyping / sub-classing?"

class Queue {
  // real private class field ...
  #items = [];

  // ... but prototypal methods.
  dequeue(item) {
    return this.#items.shift(item);
  }
  enqueue(item) {
    this.#items.push(item);
    return item;
  }
}

// a more convenient and safer subtyping / sub-classing.
class AsyncQueue extends Queue {
  constructor() {
    super();

    this.awaiters = new Queue;
  }
  // prototypal method again.
  enqueue(item) {
    const awaiter = this.awaiters.dequeue();

    if ('function' === typeof awaiter) {

      setTimeout(awaiter, 0, item);
    } else {
      super.enqueue(item);
    }
    return item;
  }
}
const asyncQueue = new AsyncQueue;

console.log(
  '  enqueue ...',
  asyncQueue.enqueue('the')   // the
);
console.log(
  '  enqueue ...',
  asyncQueue.enqueue('quick') // quick
);
console.log(
  '  enqueue ...',
  asyncQueue.enqueue('brown') // brown
);
console.log(
  '  enqueue ...',
  asyncQueue.enqueue('fox')   // fox
);
console.log(
  '- dequeue ...',
  asyncQueue.dequeue()        // the
);
console.log(
  '  enqueue ...',
  asyncQueue.enqueue('jumps') // jumps
);
console.log(
  '- dequeue ...',
  asyncQueue.dequeue()        // quick
);

console.log(
  '    awaiters enqueue ...',
  asyncQueue.awaiters .enqueue(
    (...args) => console.log({ args })        
  )                           // (...args) => console.log({ args })
);
console.log(
  '  enqueue ...',
  asyncQueue.enqueue('over')  // over
);
console.log(
  '- dequeue ...',
  asyncQueue.dequeue()        // brown
);
// async, timeout based, logging { "args": ["over"] }
.as-console-wrapper { min-height: 100%!important; top: 0; }

  • Related