You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在JavaScript中传递绑定方法作为参数并正确调用?

如何在JavaScript中传递绑定方法作为参数并正确调用?

你遇到的核心问题是bind的参数顺序搞混了,再加上对Mongoose模型的一些属性不熟悉,导致原本的绑定方法被当成了字符串,自然会报queryFunction is not a function的错误。我来一步步帮你拆解和修复:

先说说问题出在哪

你在makeCachable里用了cachedQuery.bind(method, ${thisObject.className}.${method.name}),这里的bind用法完全搞反了:
bind的语法是函数.bind(要绑定的this上下文, 预设参数1, 预设参数2...),它返回的新函数被调用时,会先把预设参数传进去。你这行代码相当于:

  1. 把cachedQuery的this绑定成了method(也就是你传入的Blog.find)
  2. 预设第一个参数为${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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.14 16:48:06