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

如何在Conv类外部定义可访问payload的转换函数?

让Conv转换函数访问完整payload(不修改原类)

需求背景

我有一个Conv类(代码见文末),通过run方法实现对象转换,实际版本包含runInverse方法(示例已省略)。

当前使用方式:

function fn(s: String, inv: Boolean) {
  return inv ? "inversed" : s;
}

let conv = new Conv();
conv.fromPathToWithFn("data.id", fn, "internal_id");

conv.run({ data: { id: "1234" }}); // => { internal_id: "1234" }

希望在不修改Conv类本身的前提下,让转换函数fn能访问run/runInverse处理的完整payload对象,例如:

function fn(s: String, inv: Boolean) {
  if (this.payload.otherValue) {
    return "nice"; 
  } else {
    return s;
  }
}

调用效果:

conv.run({ data: { id: "1234" }, otherValue: "defined" }); // => { internal_id: "nice" }

解决方案

核心思路是在转换函数执行时,将完整payload注入到函数的执行上下文中,同时避免修改原Conv类代码。以下是两种可行方案:

方案1:通过闭包绑定上下文

利用闭包在run执行时传递payload,同时包装转换函数:

// 包装转换函数,使其能访问conv实例的payload
function createBoundConverter(conv, originalFn) {
  return function(s, inv) {
    // 将this指向conv实例,同时conv上已挂载payload
    return originalFn.call(conv, s, inv);
  };
}

// 扩展Conv的run方法,挂载payload到实例
const originalRun = Conv.prototype.run;
Conv.prototype.run = function(payload, parentString = "") {
  this.payload = payload; // 挂载完整payload
  const result = originalRun.call(this, payload, parentString);
  delete this.payload; // 执行后清理,避免污染实例
  return result;
};

// 使用示例
let conv = new Conv();
const boundFn = createBoundConverter(conv, function(s, inv) {
  if (this.payload.otherValue) {
    return "nice";
  } else {
    return s;
  }
});
conv.fromPathToWithFn("data.id", boundFn, "internal_id");

conv.run({ data: { id: "1234" }, otherValue: "defined" }); // 输出 { internal_id: "nice" }

方案2:动态包装转换函数

在run执行时动态替换转换函数,注入payload上下文:

// 扩展fromPathToWithFn,标记需要绑定上下文的函数
const originalFromFn = Conv.prototype.fromPathToWithFn;
Conv.prototype.fromPathToWithFn = function(from, fn, to) {
  originalFromFn.call(this, from, { isBound: true, fn }, to);
};

// 重写run方法,动态包装转换函数
const originalRun = Conv.prototype.run;
Conv.prototype.run = function(payload, parentString = "") {
  // 临时包装转换函数,注入payload上下文
  this.converted = this.converted.map(item => {
    if (item.fn?.isBound) {
      return {
        ...item,
        fn: (s, inv) => item.fn.fn.call({ payload }, s, inv)
      };
    }
    return item;
  });

  const result = originalRun.call(this, payload, parentString);

  // 恢复原始转换函数,避免影响后续调用
  this.converted = this.converted.map(item => {
    if (item.fn?.isBound) {
      return { ...item, fn: item.fn.fn };
    }
    return item;
  });

  return result;
};

// 使用示例
let conv = new Conv();
function fn(s, inv) {
  if (this.payload.otherValue) {
    return "nice";
  } else {
    return s;
  }
}
conv.fromPathToWithFn("data.id", fn, "internal_id");

conv.run({ data: { id: "1234" }, otherValue: "defined" }); // 输出 { internal_id: "nice" }

原Conv类代码

export class Conv {
  converted: Array<any> = []; // 转换规则集合
  inverse: Boolean = false; // 是否启用反向转换
  newPayload: object = {}; // 转换结果对象

  constructor() { }

  /**
   * 将值从源路径复制到目标路径
   * @param {String} from 源路径
   * @param {String} to 目标路径
   */
  fromPathTo(from: String, to: String) {
    this.converted.push({ from, to });
  }

  /**
   * 读取源路径值,经转换函数处理后存入目标路径
   * @param {String} from 源路径
   * @param {Function} fn 转换函数
   * @param {String} to 目标路径
   */
  fromPathToWithFn(from: String, fn: Function, to: String) {
    this.converted.push({ from, fn, to });
  }

  /**
   * 执行对象转换
   * @param {Object} payload 待转换对象
   * @returns {Object} 转换后的对象
   */
  run(payload: Object, parentString: String = "") {
    this.inverse = false;

    if (typeof payload !== "object" || payload === null || Array.isArray(payload)) {
      return payload;
    }

    for (let prop in payload) {
      let currentPath: string = prop;
      if (parentString !== "") {
        currentPath = parentString + "." + prop;
      }
      if (typeof payload[prop] === "object" && !Array.isArray(payload[prop])) {
        this.run(payload[prop], currentPath);
      } else {
        const newProp: string = this.converted.find(_ => _.from === currentPath)?.to;
        const func: Function | undefined = this.converted.find(_ => _.from === currentPath)?.fn;
        const def: String | null = this.converted.find(_ => _.from === currentPath)?.def;

        if (newProp === undefined || newProp === null) {
          break;
        }

        if (newProp && newProp !== prop) {
          typeof func !== "undefined"
            ? lodash.set(
              this.newPayload,
              newProp,
              func(this.run(lodash.get(payload, prop)), this.inverse)
            )
            : this.run(lodash.get(payload, prop)) === null || typeof this.run(lodash.get(payload, prop)) === "undefined"
              ? lodash.set(this.newPayload, newProp, def)
              : lodash.set(this.newPayload, newProp, this.run(lodash.get(payload, prop)));
        } else {
          typeof func !== "undefined"
            ? lodash.set(
              this.newPayload,
              prop,
              func(this.run(lodash.get(payload, prop)), this.inverse)
            )
            : this.run(lodash.get(this.newPayload, currentPath)) === null || typeof this.run(lodash.get(this.newPayload, prop)) === "undefined"
              ? lodash.set(this.newPayload, currentPath, def)
              : lodash.set(this.newPayload, currentPath, this.run(lodash.get(payload, prop)));
        }
      }
    }
    return this.newPayload;
  }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 02:07:02