为什么javascript调用方法无法在数组原型方法上执行

Why javascript call method fails to execute on array prototype method

提问人:Validus Oculus 提问时间:11/18/2022 更新时间:11/20/2022 访问量:47

问:

我正在玩 JS 功能以提高我对语言的理解。

在下面的示例中,我尝试使用方法在给定的数组对象上启动数组函数。我知道完成工作的替代方案;但是,我很好奇为什么我编写的代码不起作用?call

谢谢!

法典

const f_timeit = (f) => {
  return function(...args) {
    util.p(`Entering function ${f.name}`);
    const start_time = Date.now();
    try {
      return f(...args);
    } finally {
      util.p(`Exiting ${f.name} after ${Date.now() - start_time}ms`);
    }
  }
};

const rarr = [...Array(10000000)].map(e => ~~(Math.random() * 20));

// following works
console.log(f_timeit((arr) => {
  return arr.reduce((acc, val) => acc + val, 0);
})(rarr));

// following fails
util.p(f_timeit(Array.prototype.reduce.call)(rarr, (acc, val) => acc + val, 0));

错误:

        return f(...args);
               ^
TypeError: f is not a function
JavaScript 数组 ecmascript-6 回调 调用

评论

0赞 Bergi 11/18/2022
该方法未绑定到函数。你还不如用过,同样的东西。顺便说一句,错误消息非常具有误导性,它不是函数,而是在不是函数的东西上调用的。callreducef_timeit(Function.prototype.call)fcall
0赞 Bergi 11/18/2022
"我知道完成工作的替代方案“——你有什么解决方案?
0赞 Validus Oculus 11/18/2022
@Bergi我指的是我上面提出的箭头函数解决方案。顺便说一句,如果我按照您的建议调用,该调用方法的参数是什么;我将如何指定调用Array.prototype.reduce?
0赞 Bergi 11/18/2022
你可以使用 call.bind(reduce)。另一个(更好的)解决方案是在 中使用 ,然后 。f.apply(this, args)f_timeitf_timeit(Array.prototype.reduce).call(rarr, …, 0)
0赞 Validus Oculus 11/19/2022
这仍然有问题f_timeit(Array.prototype.reduce).call(rarr, (acc, val) => acc + val, 0)

答:

2赞 Bergi 11/20/2022 #1

错误消息有点误导,不是“不是函数”,而是在不是函数的东西上调用的方法 - 特别是.这是由于 this 参数的工作方式,特别是因为访问对象上的方法不会自动绑定它们ffFunction.prototype.callundefined

因此,实现您想要的各种方法

f_timeit(() => rarr.reduce((acc, val) => acc + val, 0))()
f_timeit((ctx) => ctx.reduce((acc, val) => acc + val, 0))(rarr)
f_timeit((ctx, reducer) => ctx.reduce(reducer, 0))(rarr, (acc, val) => acc + val)
f_timeit((ctx, reducer, init) => ctx.reduce(reducer, init))(rarr, (acc, val) => acc + val, 0)
f_timeit((ctx, ...args) => ctx.reduce(...args))(rarr, (acc, val) => acc + val, 0)
f_timeit((ctx, ...args) => Array.prototype.reduce.apply(ctx, args))(rarr, (acc, val) => acc + val, 0)
f_timeit((ctx, ...args) => Array.prototype.reduce.call(ctx, ...args))(rarr, (acc, val) => acc + val, 0)
f_timeit((...args) => Array.prototype.reduce.call(...args))(rarr, (acc, val) => acc + val, 0)
// f_timeit(Array.prototype.reduce.call)(rarr, (acc, val) => acc + val, 0) - no!
f_timeit(Array.prototype.reduce.call.bind(Array.prototype.reduce))(rarr, (acc, val) => acc + val, 0) // yes!
f_timeit(Function.prototype.call.bind(Array.prototype.reduce))(rarr, (acc, val) => acc + val, 0)

但是,我通常建议您更改以尊重调用返回函数时传递的值:f_timeitthis

const f_timeit = (f) => {
  return function(...args) {
    util.p(`Entering function ${f.name}`);
    const start_time = Date.now();
    try {
      return f.call(this, ...args);
//                  ^^^^
      return f.apply(this, args); // or equivalently
    } finally {
      util.p(`Exiting ${f.name} after ${Date.now() - start_time}ms`);
    }
  }
};

这样,您可以使用

f_timeit(Array.prototype.reduce).call(rarr, (acc, val) => acc + val, 0)