Javascript console output before and after method call with AOP

感情迁移 提交于 2019-12-22 13:49:11

问题


I would like to measure the computing time of methods.

A nice way is (How do you performance test JavaScript code?) with console.time('Function #1'); and console.timeEnd('Function #1');

My idea is to add these console outputs on lifecycle-methods. In this case using SAPUI5 like createContent:funtion(){}; methods.

This should be possible with AOP using before() and after() to runt the time counting.

Which AOP framework would you suggest and how to implement it with the need of modifying the identification string "Function #1" automatically?


回答1:


There actually is no need for aspects in Javascript since you can change any function of any object at any time. JavaScript prototypes allows you to manipulate method implementations of all instances of an object at runtime. Here are two approaches for what you plan.

You could use a generic wrapper function:

var measureId = 0;
var fnMeasureFunction = function(fnToMeasure) {
  console.time('measure'+ measureId);
  fnToMeasure();
  console.timeEnd('measure'+ measureId);
  measureId++;
}

Admittedly that requires you to change your actual code...

For static functions or functions that belong to a prototype you could also do sth. like this from the outside without the need of any change to your existing code:

// any static function
var measureId = 0;
var fnOriginalFunction = sap.ui.core.mvc.JSViewRenderer.render;
sap.ui.core.mvc.JSViewRenderer.render = function() {
  console.time('measure'+ measureId);
  fnOriginalFunction.apply(this, arguments);
  console.timeEnd('measure'+ measureId);
  measureId++;
}

// any prototype function
var fnOriginalFunction = sap.m.Button.prototype.ontouchstart;
sap.m.Button.prototype.ontouchstart= function() {
  console.time('measure'+ measureId);
  fnOriginalFunction.apply(this, arguments);
  console.timeEnd('measure'+ measureId);
  measureId++;
}



回答2:


This should be possible with AOP using before() and after() to runt the time counting.

As it already got mentioned, one really is not in need of real Aspect-oriented Programming in order to solve such tasks in JavaScript. But this language might deserve some more standardized method-modifiers in addition to the already existing bind method.

Please check back with my 2 most recent posts on this matter:

  • sandwich pattern in javascript code
  • Can you alter a Javascript function after declaring it?

... and how to implement it with the need of modifying the identification string "Function #1" automatically?

One does not need to since the console's time / timeEnd functionality only has to have identical entry and exit points for measuring time (like the start/stop trigger of a stopwatch). So one gets along with exactly the reference of the function/method one is currently running/measuring.

In order to solve the given task I will suggest around only instead of both before and after for the former generates less overhead. The next code block exemplarily shows a possible prototypal implementation. It also is the base for the afterwards following example that finally might solve the OP's task.

(function (Function) {
  var
    isFunction = function (type) {
      return (
           (typeof type == "function")
        && (typeof type.call == "function")
        && (typeof type.apply == "function")
      );
    },
    getSanitizedTarget = function (target) {
      return ((target != null) && target) || null;
    }
  ;
  Function.prototype.around = function (handler, target) { // [around]
    target  = getSanitizedTarget(target);

    var proceed = this;
    return (isFunction(handler) && isFunction(proceed) && function () {

      return handler.call(target, proceed, handler, arguments);

    }) || proceed;
  };
}(Function));

The next example takes into account that method-modification essentially relies on functionality that is bound to an object. It is not just function wrapping. In order to not loose the context a method is operating on, context has to be delegated / passed around as target throughout all operations.

For this the example does not modify calculate since it is not bound to an object but it modifies trigger instead.

var testObject = {

  calculate: function (hugeInteger) {
    var
      i = hugeInteger,
      k = 0
    ;
    while (i--) {
      k++;
    }
    return k;
  },
  trigger: function (hugeInteger) {
    this.result = this.calculate(hugeInteger);
  },
  result: -1
};

console.log("testObject.result : ", testObject.result);

console.log("testObject.trigger(Math.pow(2, 26)) : ", testObject.trigger(Math.pow(2, 26))); // takes some time.
console.log("testObject.result : ", testObject.result);

console.log("testObject.someTrigger(0) : ", testObject.trigger(0)); // logs immediately after.
console.log("testObject.result : ", testObject.result);


testObject.trigger = testObject.trigger.around(function (proceed, interceptor, args) {

  // before:
  console.time(proceed);

  // proceed:
  proceed.apply(this, args);

  // after:
  console.timeEnd(proceed);

}, testObject); // omitting the 2nd argument - the [target] object - might break code that did work before.


console.log("testObject.trigger(Math.pow(2, 26)) : ", testObject.trigger(Math.pow(2, 26)));
console.log("testObject.result : ", testObject.result);


来源:https://stackoverflow.com/questions/26777671/javascript-console-output-before-and-after-method-call-with-aop

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!