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

JavaScript中如何条件性拦截类方法链中的前置方法?

实现支持无限链式调用的SQL查询构建器

问题背景

现有一套JavaScript类尝试构建SQL查询,但无法实现预期的链式调用效果:调用from后无法继续调用where,where后无法多次调用and。需要实现如下链式调用逻辑:

const db = new Database(client);

await db.select().from(users); // 返回所有用户
await db.select().from(users).where("id = 1"); // 返回符合where条件的用户
await db.select().from(users).where("id = 1").and("email like '%gmail%'"); // 带and条件的查询
await db.select().from(users).where("id = 1").and("email like '%gmail%'").and("type = 'END_USER'"); // 无限and链式调用

解决方案代码

利用Proxy拦截和Promise thenable接口实现延迟执行查询,同时支持链式调用:

class QueryBuilder {
  #client;
  #table = null;
  #conditions = [];

  constructor(client) {
    this.#client = client;

    // 使用Proxy拦截属性访问,实现链式调用与延迟执行
    return new Proxy(this, {
      get(target, prop) {
        // 当访问then方法时(await会自动触发),执行最终查询
        if (prop === 'then') {
          return async (...args) => {
            const sql = target.#buildSql();
            const result = await target.#client.query(sql);
            return Promise.resolve(result).then(...args);
          };
        }

        // 绑定已定义的方法到当前实例
        if (typeof target[prop] === 'function') {
          return target[prop].bind(target);
        }

        // 其他属性直接返回
        return Reflect.get(target, prop);
      }
    });
  }

  select() {
    return this;
  }

  from(table) {
    this.#table = table;
    return this;
  }

  where(condition) {
    this.#conditions.push(condition);
    return this;
  }

  and(condition) {
    this.#conditions.push(`AND ${condition}`);
    return this;
  }

  // 私有方法:根据当前状态构建SQL语句
  #buildSql() {
    let sql = `SELECT * FROM "${this.#table}"`;
    if (this.#conditions.length > 0) {
      sql += ` WHERE ${this.#conditions.join(' ')}`;
    }
    sql += ';';
    return sql;
  }
}

class Database {
  #client;

  constructor(client) {
    this.#client = client;
  }

  select() {
    return new QueryBuilder(this.#client);
  }
}

代码说明

  1. QueryBuilder核心逻辑

    • 用私有字段存储查询状态:数据库客户端、目标表名、查询条件数组。
    • select/from/where/and方法仅更新查询状态,然后返回当前实例(Proxy包装后),实现链式调用。
    • #buildSql私有方法根据收集的状态动态生成完整SQL语句。
  2. Proxy拦截机制

    • 在QueryBuilder构造函数中返回Proxy实例,拦截属性访问。
    • 当访问then方法时(await操作会自动调用then),触发SQL构建与执行,将查询结果通过Promise返回。
    • 确保所有链式方法绑定到当前实例,保证状态更新的正确性。
  3. Database类封装

    • 仅负责创建QueryBuilder实例,简化外部调用入口。

测试示例

// 模拟数据库客户端
const client = {
  async query(sql) {
    console.log('执行SQL:', sql);
    // 替换为实际数据库查询逻辑
    return { rows: [] };
  }
};

const db = new Database(client);
const users = 'users';

// 测试链式调用
await db.select().from(users);
// 输出:执行SQL: SELECT * FROM "users";

await db.select().from(users).where("id = 1");
// 输出:执行SQL: SELECT * FROM "users" WHERE id = 1;

await db.select().from(users).where("id = 1").and("email like '%gmail%'");
// 输出:执行SQL: SELECT * FROM "users" WHERE id = 1 AND email like '%gmail%';

await db.select().from(users).where("id = 1").and("email like '%gmail%'").and("type = 'END_USER'");
// 输出:执行SQL: SELECT * FROM "users" WHERE id = 1 AND email like '%gmail%' AND type = 'END_USER';

内容的提问来源于stack exchange,提问作者xqcccccccccc

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 19:07:50