Click here to Skip to main content
11,501,513 members (72,277 online)
Click here to Skip to main content

Proxy pattern in JavaScript

, 21 Nov 2013 CPOL 16.7K 152 11
Rate this:
Please Sign up or sign in to vote.
With one function call, wrap a class with a proxy in Javascript. A precursor to easy error mocking in node.js using mocha

Introduction  

This is a very simple script to allow you to create a proxy for a class in JavaScript. Let's say we have a class (function that will be used as a constructor) Math which has a few functions defined on it such as add, subtract, multiply, and divide.    

function Math() {
  this.IsMath = true;
}
Math.prototype = {
  add: function(a, b) {
    return a + b;
  },
  subtract: function(a, b) {
    return this.add(a, -b);
  },
  multiply: function(a, b) {
    return a * b;
  },
  divide: function(a, b) {
    return a / b;
  }
} 

We want to be able to modify it's behavior in a way that will allow us to intercept each function call within this class with a beforeFunction event and an afterFunction event. 

We can decide to do whatever we want with these two functions. You can visit this tip here which makes use of this generic proxy script to allow you to mock errors in mocha (node.js) very easily. For this article, I will define a simple proxy that logs function calls (this is done in node.js using console.log, but it can be modified easily for in browser code) . 

Using the code

One function named createProxyDefinition is all you need. This function is defined in proxy.js which you can download from the files attached with this article. The function takes four parameters, prefixbeforeFunction, afterFunction, and more.

beforeFunction and afterFunction are pretty explanatory. Whenever a function is called of your target class, these two events will fire accordingly. The prefix is a unique name identifier for the proxy you want to create, this will allow you to "add" more than one proxy to the same class. 

beforeFunction will be given three parameters, the first one is the function name being called, the second one is an array of arguments that is passed to the function and the third parameter is an array of the parameter names. If you return a value from your event, the original function will not be called and your return value will be used instead.

afterFunction will be given four parameters, the first one is the function name being called, the second one is the return value from the original function, the third one is the array of arguments that is passed to the function and the fourth parameter is an array of the parameter names. 

The more is any extra object, it will extend the prototype of the class passed in.

An example works out best to explain how to use the class. I want to create a simple proxy definition that just prints out the function names being called along with their parameters, it can be useful for tracing function calls. Another more useful example, which is the reason why I wanted to write this article in the first place is to allow me to do full code coverage when writing node.js projects, please refer to the tip here. 

To create a proxy definition that logs files (node.js module)   

var beforeFunction = function(name, args, paramsArray) {
  if(!this.__levels) {
    this.__levels = 0;
  }
  for(var i = 0; i < this.__levels; i++) {
    process.stdout.write("\t");
  }
  console.log(" Entering: >> ", name, " : ", args);
  this.__levels++;
};
var afterFunction = function(name, ret, args, paramsArray) {
  this.__levels--;
  for(var i = 0; i < this.__levels; i++) {
    process.stdout.write("\t");
  }
  console.log(" Exiting: >> ", name)
}
var createCallsLogger = createProxyDefinition("callslogger", beforeFunction, afterFunction);

createCallsLogger is now a function that takes any constructor and modifies it as needed.

A sample usage for this would be: 

createCallsLogger(Math);
var math = new Math();
math.add(1, 2);
math.subtract(10, 3);
math.multiply(5, 5);
math.divide(16, 4);

Now any function calls made on math will be logged to the console, a sample output follows:

usr/bin/node index.js
 Entering: >> add  :  { '0': 1, '1': 2 }
 Exiting:  >> add
 Entering: >>  subtract  :  { '0': 10, '1': 3 }
	 Entering: >> add  :  { '0': 10, '1': -3 }
	 Exiting:  >> add
 Exiting:  >> subtract
 Entering: >> multiply  :  { '0': 5, '1': 5 }
 Exiting:  >> multiply
 Entering: >> divide  :  { '0': 16, '1': 4 }
 Exiting:  >> divide
Process finished with exit code 0 

Under the hood 

The createProxyDefinition function takes a class definition (constructor function) and goes over all of it's prototype functions, replaces each function with a new function that does the delegation of the calls and events needed. This function is in the attached file proxy.js. Roughly speaking this is what it does in pseudo-code. 

function __createProxy(constructor, proxyInstanceDefinition, defPrefix) 
{
  if (proxy already added)
    return;
  for each function in constructor.prototype
  {
      var newName = __ + functionname;
      copy original function reference to a new function with name "NewName"
      create a new function to replace the current. the new function will call original function)      
  }
}

Points of Interest 

This proxy creation method is not intended to be used in production code, ideally it is used for quickly mocking or tapping into other classes, like the case of using it to mock errors in node.js to achieve full coverage. The reason that you are not advised to use it in production code is because it basically creates new function instances for each function to be overridden, so ideally speaking, in a production environment you should have custom written code to do such things, to achieve exactly what you want without the extra overhead of redirection and memory usage. 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Ralph Varjabedian
Technical Lead
Lebanon Lebanon
No Biography provided

Comments and Discussions

 
GeneralPraise Pin
Tariq Newaz Shahriar3-Nov-13 6:23
memberTariq Newaz Shahriar3-Nov-13 6:23 
GeneralRe: Praise Pin
Ralph Varjabedian11-Nov-13 18:41
memberRalph Varjabedian11-Nov-13 18:41 
Questionnice Pin
patelraj16-Oct-13 21:04
memberpatelraj16-Oct-13 21:04 
AnswerRe: nice Pin
Ralph Varjabedian20-Nov-13 5:40
memberRalph Varjabedian20-Nov-13 5:40 
QuestionProxy extract Pin
Robert Hoffmann16-Oct-13 5:43
memberRobert Hoffmann16-Oct-13 5:43 
AnswerRe: Proxy extract Pin
Ralph Varjabedian16-Oct-13 7:54
memberRalph Varjabedian16-Oct-13 7:54 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web04 | 2.8.150520.1 | Last Updated 21 Nov 2013
Article Copyright 2013 by Ralph Varjabedian
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid