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); } }
代码说明
QueryBuilder核心逻辑
- 用私有字段存储查询状态:数据库客户端、目标表名、查询条件数组。
select/from/where/and方法仅更新查询状态,然后返回当前实例(Proxy包装后),实现链式调用。#buildSql私有方法根据收集的状态动态生成完整SQL语句。
Proxy拦截机制
- 在QueryBuilder构造函数中返回Proxy实例,拦截属性访问。
- 当访问
then方法时(await操作会自动调用then),触发SQL构建与执行,将查询结果通过Promise返回。 - 确保所有链式方法绑定到当前实例,保证状态更新的正确性。
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
相关产品推荐
相关产品推荐

