Skip to content
导航栏

表达式

在 form 中的字段进行条件显示时,visibleOn 的值可以使用表达式。

表达式首先需要经过语法处理器编译

js
// \amis\packages\amis-formula\src\parser.ts
js
// amis/packages/amis-core/src/utils/formula.ts

/**
 * formulaExec 运算器:根据当前字符串类型执行对应运算,也可按指定执行模式执行运算
 *
 * 运算模式(execMode)支持以下取值:
 * 1. tpl: 按模板字符串执行(JavaScript 模板引擎),比如:Hello ${amisUser.email}、<h1>Hello</h1>, <span>${amisUser.email}</span>;
 *    备注: 在模板中可以自由访问变量,详细请见:https://www.lodashjs.com/docs/lodash.template;
 * 2. formula: 按新版公式表达式执行,用于执行 ${ xxx } 格式的表达式;
 *    支持从window、localStorage、sessionStorage获取数据,比如:${num1 + 2}、${ls:env}、${window:document}、${window:document.URL}、${amisUser.email};
 *    详细请见:https://aisuda.bce.baidu.com/amis/zh-CN/docs/concepts/data-mapping#namespace
 * 3. evalFormula: 按新版公式表达式执行,用于执行 ${ xxx } 和 非${ xxx } 格式的表达式(evalMode 为 true,不用 ${} 包裹也可以执行),功能同 formula 运算模式;
 * 4. js: 按Javascript执行,表达式中可以通过data.xxx来获取指定数据,并且支持简单运算;
 *    比如:data.num1 + 2、this.num1 + 2、num1 + 2;(备注:三个表达式是等价的,这里的 this 就是 data。)
 * 5. var: 以此字符串作为key值从当前数据域data中获取数值;性能最高(运行期间不会生成ast和表达式运算);
 * 6. true 或者 false: 当execMode设置为true时,不用 ${} 包裹也可以执行表达式;
 * 7. collect: 用于从表达式中获取所有变量;
 *
 * 备注1: 用户也可以使用 registerFormulaExec 注册一个自定义运算器;
 * 备注2: 模板字符串 和 Javascript 模板引擎 不可以交叉使用;
 * 备注3: amis 现有的 evalFormula 方法,可执行 ${} 格式类表达式,但不支持 filter 过滤器,所以这里用 resolveValueByName 实现;
 * 备注4: 后续可考虑将 amis现有的运算器都放这里管理,充当统一的运算器入口。
 */

js: (expression: string, data?: object) => {
  let debug = false;
  const idx = expression.indexOf('debugger');
  if (~idx) {
    debug = true;
    expression = expression.replace(/debugger;?/, '');
  }

  let fn;
  if (expression in FORMULA_EVAL_CACHE) {
    fn = FORMULA_EVAL_CACHE[expression];
  } else {
    fn = new Function(
      'data',
      'utils',
      `with(data) {${debug ? 'debugger;' : ''}return (${expression});}`,
    );
    FORMULA_EVAL_CACHE[expression] = fn;
  }

  data = data || {};

  let curResult = undefined;
  try {
    // this总是指向函数定义时指向的data对象,
    curResult = fn.call(data, data, getFilters());
  } catch (e) {
    console.warn(
      '[formula:js]表达式执行异常,当前表达式: ',
      expression,
      ',当前上下文数据: ',
      data,
    );
    return expression;
  }
  return curResult;
};

在 js 执行模式下,表达式可以直接访问 data 中的数据,也可以使用简单的运算。它会使用 new Function 来动态创建一个函数,将表达式嵌入函数体中执行。

从源代码中分析得知,可以在表达式中使用 debugger 开启调试,但是跟数学函数会有冲突。

在 amis 中,表达式使用的是 JavaScript 语法,支持使用变量和函数。 其中 this 跟 data 两个变量未跟预期一样是表示同样的对象。

表达式中 this 总是指向函数定义引用的变量域,比如是 form 对象。但是表达式中的 data 对象却是指向 data 同名变量。如果一个 form 在提交后,data 对象发生了变化,在接口中返回一个 data 对象,这时 data 变量会变覆盖!