“Javascript - The Definitive Guide”-未捕获的类型错误:trace(...)不是函数

rqdpfwrv  于 2023-04-28  发布在  Java
关注(0)|答案(2)|浏览(109)

我正在尝试“Javascript -权威指南”第8章函数中的示例。在第8节。3.4“函数调用的扩展运算符”,代码是可行的:

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);

控制台输出为:

Entering function benchmark
Exiting benchmark after 46ms

在第8节。7.4“The call()and apply()Methods”,函数timed()被更改为trace(),我无法让代码工作:

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);

抛出此错误:
未捕获的类型错误:trace(...)不是函数
我尝试了几个变种,但都不起作用:

benchmark.trace(1000000);
trace.benchmark(1000000);
benchmark.call(trace, 1000000);
obj = {};
trace(obj, benchmark(1000000));

我做了一个检查:

console.log(typeof trace);

输出表明trace()是一个函数。..
有人能解释一下trace()应该如何与benchmark()一起使用吗?我是否需要创建新的对象或函数?
这本书只提供了这样的提示:
下面定义的trace()函数类似于§8中定义的timed()函数。3.4,但它适用于方法而不是函数。它使用apply()方法而不是spread运算符,通过这样做,它能够调用 Package 方法,并使用与 Package 方法相同的参数和相同的this值:

// 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) { ...
8aqjt8rx

8aqjt8rx1#

因此,更新后的方法trace应该接受一个对象(o)和该对象上的一个方法(m),它的设计目的是用基准测试功能来装饰该方法。
下面是如何使用它:

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);

我们可以进一步分解它是如何工作的

let original = o[m];

当传入对象和方法名时,上面的一行捕获一个局部变量,并带有指向该函数的指针

o[m] = function(...args) {
   ....
}

上面的部分用一个指向新函数的指针替换对象上的那个函数

let result = original.apply(this, args);

上面的一行调用原始函数(using apply),并传递了参数。您会注意到,is的顶部和尾部是console.log,您使用它来查看方法何时进入和退出。最后,新附加的方法返回调用original方法的结果。

wnavrhmk

wnavrhmk2#

重要的是,代码示例使用的是函数表达式语法,而不是箭头函数表达式语法。如果你使用箭头函数,并且原始函数使用“this”关键字,这将不起作用,因为this的值将接受词法作用域并表示外部作用域中this的值,在这个例子中,严格模式下将是未定义的,而在非严格模式下将是全局对象。
通过使用带有function关键字的函数表达式语法,this的值是接收者,在本例中是myObject。现在,apply完全可以将未定义的值作为其第一个参数。在这个例子中,这并不重要,因为原始函数没有使用this关键字,但如果它使用了,你可能会在使用它时出错或意外结果:

function trace(o, m) {
   let original = o[m];
   o[m] = (...args) => {
     console.log(new Date(), "Entering:", m);
     let result = original.apply(this, args);
     console.log(new Date(), "Exiting:", m);
     return result;
   };
  }
  
  const myObject = {
     a: 1,
     benchmark: (n) => {
        let sum = 0 + this.a;
        for(let i = 1; i <= n; i++) sum += i;
        return sum;
     }
  }
  
  trace(myObject,"benchmark");
  myObject.benchmark(1000000);

2023-04-27T17:01:01.575Z Entering: benchmark
2023-04-27T17:01:01.581Z Exiting: benchmark
NaN

相关问题