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



我正在尝试"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节中;call((和apply((方法";,函数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);

引发此错误:

未捕获类型错误:跟踪(…(不是函数

我尝试了一些变体,但都不起作用:

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

我用这个做了一个检查:

console.log(typeof trace);

输出表明trace((是一个函数。。。

有人能解释一下trace((应该如何与benchmark((一起使用吗?我需要创建新的对象或函数吗?

这本书只提供了以下提示:

下面定义的trace((函数类似于§8.3.4中定义的timed((函数,但它适用于方法而不是函数。它使用apply((方法而不是spread运算符,通过这样做,它能够调用具有与包装方法相同参数和相同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) { ...

因此,更新后的方法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);

上面的行调用了带有传入参数的原始函数(使用apply(。您会注意到,is的顶部和尾部都有console.log,您使用它来查看方法的输入和退出时间。最后,新附加的方法返回调用original方法的结果。

重要的是,代码示例使用的是函数表达式语法,而不是箭头函数表达式语法。如果您正在使用箭头函数,而原始函数使用";这个";关键字,因为this的值将承担词法作用域,并表示来自外部作用域的this的值,在本例中,在严格模式下,外部作用域将是未定义的,在非严格模式下将是全局对象。

通过使用带有function关键字的函数表达式语法,this的值就是接收器,在本例中是myObject。现在,将一个未定义的值作为其第一个参数,应用程序就完全可以了。在这个例子中,这并不重要,因为原始函数没有使用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

最新更新