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

nuypyhwy  于 2022-11-20  发布在  Java
关注(0)|答案(1)|浏览(141)

我正在玩JS的特性来提高我对语言的理解。
在下面的例子中,我尝试使用call方法来初始化给定数组对象上的Array函数。然而,我很好奇为什么我写的代码不起作用?。
谢谢你,谢谢你

代码

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
r8xiu3jd

r8xiu3jd1#

这个错误消息有点误导人,它并不是说“f不是一个函数”,而是说fFunction.prototype.call方法,它被调用的对象不是一个函数--特别是undefined。这是由于how the this argument works,特别是accessing methods on an object does not automatically bind them
所以实现你想要的各种方法

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_timeit,以便在调用返回的函数时考虑传递的this值:

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)

相关问题