【问题标题】:Lifecycle of a dynamic created function in a for loop with eval带有 eval 的 for 循环中动态创建函数的生命周期
【发布时间】:2026-02-26 07:15:02
【问题描述】:

我正在尝试为我的课堂创建仪表板(异步开始、异步持续时间、异步测试)...

我的问题是我使用eval 动态创建函数(我知道这是通向黑暗面的方法)。那些动态创建的函数是否也不再存在于创建它们的 for 循环中?

     function onOpen() {
      const FUNC_STR = 'course';
      var evalString = '';
      const response = Classroom.Courses.list();
      const courses = response.courses;
      for(var index  in courses) {
        const course = courses[index]
        evalString += 'function ' + FUNC_STR + course.id + '() { ' + FUNC_STR + '(' + course.id + ') }';
        eval(evalString);
      }
      const ui = SpreadsheetApp.getUi();
      var mymenu = ui.createMenu('Classroom');
      if (courses && courses.length > 0) {
        for (i = 0; i < courses.length; i++) {
          const course = courses[i];
          mymenu.addItem(course.name, FUNC_STR + course.id);
          }
        mymenu.addToUi();
        }
    }
    function course(id ) {
      SpreadsheetApp.getActiveSheet().getRange('A1').setValue(Classroom.Courses.get(id).name);
    }

【问题讨论】:

  • 请放心,这不是通向黑暗面的方法 - 它黑暗面:糟糕的语言标记被拼接成一个弗兰肯斯坦怪物,被评估为生命,活着一点点将控制权传递给他们的黑暗生成并在下一次迭代之前死亡并成为要收集的垃圾。起码用Function构造函数……但是为什么要在2020年创建这样的函数,有什么理由这样做吗?
  • 开玩笑的,只是don't use eval。由于函数是 JS 中的一等公民,如果你评估一个表示函数声明的字符串,你会得到一个 Function 对象。但除非它是从循环外部引用的,否则它最终会被垃圾回收。
  • 你使用的是什么运行时v8(新)或rhino(旧)?
  • 虽然我不确定我是否能正确理解你的目标,但根据我对你目标的猜测,我提出了一个修改后的脚本,没有使用eval 作为答案。你能确认一下吗?如果我误解了您的问题并且这不是您期望的方向,我深表歉意。

标签: google-apps-script eval


【解决方案1】:

我相信你的目标如下。

  • 您希望在打开 Google 电子表格时在 Google 电子表格上创建自定义菜单。
  • 您希望通过动态创建函数来创建自定义菜单。

对于这个,这个答案怎么样?

修改点:

  • 从自定义菜单运行该函数时,需要将该函数设置为脚本的this
    • 在您的脚本中,evalString += 'function ' + FUNC_STR + course.id + '() { ' + FUNC_STR + '(' + course.id + ') }'; 的功能未设置为this。因此,在自定义菜单中运行该功能时,会出现类似“无功能”的错误。
  • 为了将功能动态设置到自定义菜单并在自定义菜单运行该功能时运行该功能,需要运行安装您要运行的功能的功能,当该功能运行在自定义菜单。
    • 在这种情况下,不需要使用eval

当你的脚本被修改后,它变成如下。在这种情况下,我通过修改这些答案的脚本来提出修改后的脚本。 Ref1Ref2

修改脚本:

请将以下脚本复制并粘贴到脚本编辑器中。请运行installFunctions() 或重新打开电子表格。由此,设置了自定义菜单。当您从自定义菜单中选择该函数时,该函数就可以运行了。

installFunctions(); // This is required to run the function. So please don't remove this.
function onOpen() {} // This can be used as the simple trigger. So please don't remove this.

function installFunctions() {
  const FUNC_STR = 'course';
  const response = Classroom.Courses.list();
  const courses = response.courses;
  const menu = SpreadsheetApp.getUi().createMenu('Classroom');
  courses.forEach(e => {
    const functionName = `FUNC_STR${e.id}`;
    this[functionName] = () => course(e.id);  // Install the function.
    menu.addItem(e.name, functionName);
  })
  menu.addToUi();
}

// This is your function.
function course(id) {
  SpreadsheetApp.getActiveSheet().getRange('A1').setValue(Classroom.Courses.get(id).name);
}

注意:

  • 在这种情况下,假设const courses = response.courses;courses 具有正确的值并且Classroom.Courses.get(id).name 有效。如果它们发生错误,请使用简单值测试脚本。

参考资料:

【讨论】: