提问人:Validus Oculus 提问时间:11/18/2022 更新时间:11/20/2022 访问量:47
为什么javascript调用方法无法在数组原型方法上执行
Why javascript call method fails to execute on array prototype method
问:
我正在玩 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
答:
2赞
Bergi
11/20/2022
#1
错误消息有点误导,不是“不是函数”,而是在不是函数的东西上调用的方法 - 特别是.这是由于 this
参数的工作方式,特别是因为访问对象上的方法不会自动绑定它们。f
f
Function.prototype.call
undefined
因此,实现您想要的各种方法
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)
评论
call
reduce
f_timeit(Function.prototype.call)
f
call
call.bind(reduce)。
另一个(更好的)解决方案是在 中使用 ,然后 。f.apply(this, args)
f_timeit
f_timeit(Array.prototype.reduce).call(rarr, …, 0)
f_timeit(Array.prototype.reduce).call(rarr, (acc, val) => acc + val, 0)