如何在JavaScript中传递绑定方法作为参数并正确调用?
如何在JavaScript中传递绑定方法作为参数并正确调用?
你遇到的核心问题是bind的参数顺序搞混了,再加上对Mongoose模型的一些属性不熟悉,导致原本的绑定方法被当成了字符串,自然会报queryFunction is not a function的错误。我来一步步帮你拆解和修复:
先说说问题出在哪
你在makeCachable里用了cachedQuery.bind(method, ${thisObject.className}.${method.name}),这里的bind用法完全搞反了:
bind的语法是函数.bind(要绑定的this上下文, 预设参数1, 预设参数2...),它返回的新函数被调用时,会先把预设参数传进去。你这行代码相当于:
- 把
cachedQuery的this绑定成了method(也就是你传入的Blog.find) - 预设第一个参数为
${thisObject.className}.${method.name}这个字符串
结果当你调用Blog.find({_user: req.user.id})时,cachedQuery拿到的参数顺序完全乱了:原本应该是函数的queryFunction变量,拿到的却是那个字符串,这当然会报错“不是函数”。
另外还有两个小坑:
- Mongoose模型的类名存在
modelName属性里,不是className,你之前的thisObject.className会返回undefined - 你写的缓存过期时间注释是4小时,但代码里是
60*60*1(1小时),这里也得统一
现在给出具体的修复步骤
1. 修复makeCachable函数
不用bind转发参数,改用匿名函数直接转发,这样参数顺序绝对不会错:
function makeCachable(method, thisObject) { // 用Mongoose的modelName获取正确的模型类名 const queryKey = `${thisObject.modelName}.${method.name}`; console.log(method, thisObject, queryKey); // 用匿名函数接收所有调用参数,再传给cachedQuery return function(...args) { return cachedQuery(method, queryKey, ...args); }; }
2. 修复cachedQuery函数的调用逻辑
你之前用queryFunction.call(queryParams)是错误的——因为queryFunction已经是绑定好this的Blog.find了,不需要再用call指定this(反而会把this改成查询参数对象,破坏Mongoose的调用逻辑),直接调用即可:
async function cachedQuery(queryFunction, queryKey, queryParams = null, cacheConfig = DEFAULT_CACHING) { const redisKey = JSON.stringify([queryKey, queryParams]); const cacheValue = await asyncGet(redisKey); if(cacheValue) { return JSON.parse(cacheValue); } // 直接调用已经绑定好的方法,参数正常传 const result = await queryFunction(queryParams); if(result) { client.set(redisKey, JSON.stringify(result), ...cacheConfig); } return result; }
3. 简化Blog.find的赋值
Mongoose的静态方法(比如find)本身已经自动绑定到模型类了,所以你不需要额外用bind(Blog),直接传Blog.find就行:
// 简化写法,完全没问题 Blog.find = caching.makeCachable(Blog.find, Blog)
如果你担心后续有其他操作修改this,保留Blog.find.bind(Blog)也不会有问题,只是多此一举而已。
4. 修正缓存过期时间
把DEFAULT_CACHING里的时间改成和注释一致的4小时:
const DEFAULT_CACHING = [ 'EX', 60 * 60 * 4,// 缓存4小时后过期 ]
修复后的完整代码
blogRoutes.js
const mongoose = require('mongoose'); const caching = require('../lib/caching') const Blog = mongoose.model('Blog'); // 直接传递Blog.find即可,无需额外bind Blog.find = caching.makeCachable(Blog.find, Blog) module.exports = app => { app.get('/api/blogs', requireLogin, async (req, res) => { let blogs = await Blog.find({_user: req.user.id}); return res.send(blogs); }); // ... other routes }
caching.js
const util = require('util'); const redis = require('redis'); const client = redis.createClient('redis://localhost:6379'); const asyncGet = util.promisify(client.get).bind(client); const DEFAULT_CACHING = [ 'EX', 60 * 60 * 4,// 缓存4小时后过期 ] function makeCachable(method, thisObject) { const queryKey = `${thisObject.modelName}.${method.name}`; console.log(method, thisObject, queryKey); return function(...args) { return cachedQuery(method, queryKey, ...args); }; } async function cachedQuery(queryFunction, queryKey, queryParams = null, cacheConfig = DEFAULT_CACHING) { const redisKey = JSON.stringify([queryKey, queryParams]); const cacheValue = await asyncGet(redisKey); if(cacheValue) { return JSON.parse(cacheValue); } const result = await queryFunction(queryParams); if(result) { client.set(redisKey, JSON.stringify(result), ...cacheConfig); } return result; } exports.makeCachable = makeCachable;
最后再提几个关键点
- 绑定方法时,除非你明确需要预设参数,否则尽量用匿名函数转发,比bind更清晰,不容易踩坑
- Mongoose模型的静态方法(比如find、create)本身已经绑定了this,不需要手动再bind
- 调用已经绑定好的方法时,直接调用就行,别画蛇添足用call/apply
备注:内容来源于stack exchange,提问作者xaviersjs
相关产品推荐
相关产品推荐

