I'm trying the examples in "Javascript - The Definitive Guide" Chapter 8 Functions. In Section 8.3.4 "The Spread Operator for Function Calls", the code was workable:
function timed(f) {
return function(...args) {
console.log(`Entering function ${f.name}`);
let startTime = Date.now();
try {
return f(...args);
}
finally {
console.log(`Exiting ${f.name} after
${Date.now()-startTime}ms`);
}
};
}
function benchmark(n) {
let sum = 0;
for(let i = 1; i <= n; i++) sum += i;
return sum;
}
timed(benchmark)(1000000);
The console output is:
Entering function benchmark
Exiting benchmark after 46ms
In Section 8.7.4 "The call() and apply() Methods", the function timed() was changed to trace() and I could not get the code to work:
function trace(o, m) {
let original = o[m];
o[m] = function(...args) {
console.log(new Date(), "Entering:", m);
let result = original.apply(this, args);
console.log(new Date(), "Exiting:", m);
return result;
};
}
trace(benchmark)(1000000);
This error is thrown:
Uncaught TypeError: trace(...) is not a function
I've tried a few variants, but all did not work:
benchmark.trace(1000000);
trace.benchmark(1000000);
benchmark.call(trace, 1000000);
obj = {};
trace(obj, benchmark(1000000));
I did a check with this:
console.log(typeof trace);
The output indicates that trace() is a function...
Could someone please explain how trace() should be used with benchmark()? Do I need to create new objects or functions?
The book only provided this Hint:
The trace() function defined in the following is similar to the timed() function defined in §8.3.4, but it works for methods instead of functions. It uses the apply() method instead of a spread operator, and by doing that, it is able to invoke the wrapped method with the same arguments and the same this value as the wrapper method:
// Replace the method named m of the object o with a version that logs
// messages before and after invoking the original method.
function trace(o, m) { ...
So the updated method trace is supposed to take an object (o) and a method on that object (m) and it is designed to kind of decorate that method with benchmarking functionality.
Here's how to use it:
function trace(o, m) {
let original = o[m];
o[m] = function(...args) {
console.log(new Date(), "Entering:", m);
let result = original.apply(this, args);
console.log(new Date(), "Exiting:", m);
return result;
};
}
const myObject = {
benchmark: function(n){
let sum = 0;
for(let i = 1; i <= n; i++) sum += i;
return sum;
}
}
// set up the trace "decorator" so that the method is traced
trace(myObject,"benchmark");
// now call the method and see the trace output
myObject.benchmark(1000000);
We can break down further how this works
let original = o[m];
When you pass in an object and a method name, the above line captures a local variable with a pointer to that function
o[m] = function(...args) {
....
}
The part above replaces that function on the object with a pointer to a new function
let result = original.apply(this, args);
The line above calls the original function (using apply) with the arguments passed in. You'll note that is is topped and tailed with the console.log which you're using to see when the method is entered and exited. Finally the newly attached method returns the result of calling the original method.