Hao Wu
Hao Wu

Reputation: 20689

How can I modify the behavior of every function in an object?

Right now I have an object, such as this Pen.

The prototype of the class contains a series of functions and other properties.

var Pen = function(){
   this.inkColor = 'red';

   this.write = function(text){
      document.write(text);
   }

   this.refill = function(){
      console.log('refilling');
   }
   
   this.getInkColor = function(){
      return this.inkColor;
   }
   
};

var pen = new Pen();
pen.write(pen.getInkColor() + ': Hello');

Is there away to avoid modifying the Pen class, but change the behavior of every function it has, such as print a log before the actual function call?

this.write = function(text){
   // do something first
   document.write(text);
}

this.refill = function(){
   // do something first
   console.log('refilling');
}

this.getInkColor = function(){
   // do something first
   return this.inkColor;
}

Upvotes: 2

Views: 57

Answers (3)

T.J. Crowder
T.J. Crowder

Reputation: 1074276

You can replace the functions with wrappers that call the original and also do something else. For instance:

Object.keys(pen).forEach(name => {
    const originalFunction = pen[name];
    if (typeof originalFunction === "function") {
        pen[name] = function(...args) {
            console.log(name, args);
            return originalFunction.apply(this, args);
        };
    }
});

That replaces all functions on pen (only its own, not ones it inherits) with wrappers that first do a console.log then call the original.

Live Example:

var Pen = function(){
   this.inkColor = 'red';

   this.write = function(text){
      // used console.log instead of document.write
      console.log(text);
   }

   this.refill = function(){
      console.log('refilling');
   }
   
   this.getInkColor = function(){
      return this.inkColor;
   }
   
};

var pen = new Pen();

Object.keys(pen).forEach(name => {
    const originalFunction = pen[name];
    if (typeof originalFunction === "function") {
        pen[name] = function(...args) {
            console.log(name, args);
            return originalFunction.apply(this, args);
        };
    }
});

pen.write(pen.getInkColor() + ': Hello');

You can tweak that to handle functions inherited from the prototype, or inherited only from Pen.prototype (you don't have anything on Pen.prototype at present), etc.

Upvotes: 2

JanS
JanS

Reputation: 2075

You could write a function that returns another function:

function doSomethingFirst(somethingToDoFirstFn, thingToDoAfterFn) {
  return function() {
    somethingToDoFirstFn.apply(null, arguments);
    thingToDoAfterFn.apply(null, arguments);
  }
} 

var Pen = function(){
   // code

   this.refill = doSomethingFirst(function(){
      console.log('somethingFirst');
   }, function() {
      console.log('refilling');
   })   

   // code
};

Upvotes: 2

bnord
bnord

Reputation: 395

You can wrap your pen in a Proxy and define an appropriate handler.

var Pen = function(){
   this.inkColor = 'red';

   this.write = function(text){
      document.write(text);
   }

   this.refill = function(){
      console.log('refilling');
   }
   
   this.getInkColor = function(){
      return this.inkColor;
   }
   
};

var handler = {
  get: function(target, name) {
    return name in target ? function (...args) {console.log('Hello World'); return target[name](args)} : undefined;
  }
};


var pen = new Pen();
var p = new Proxy(pen, handler);
p.write(p.getInkColor() + ': Hello');

Upvotes: 4

Related Questions