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

如何在page.evaluate中定义命名函数避免ReferenceError?

问题排查与解决

编辑补充:问题根源与修复方案

经排查,该问题由使用tsx运行代码导致,与Playwright无关。可通过npx tsx <file-with-code>.ts复现问题。

可自行定义__name来解决该问题:

declare global {
  interface Window {
    __name: (func: Function)=> Function 
  }
}

//... 获取page或context后:
 await context.addInitScript(() => {
      window.__name = (func) => func;
    })

相关问题涉及:

  • ESBuild
  • tsx
  • Playwright

原始问题

我拥有一个Playwright的Page对象,想要在其上执行代码。由于需要在evaluate中多次执行某段逻辑,我尝试定义一个函数,代码如下:

await page.evaluate(() => {
    const foo = (): string => "foo";

    return foo();
  });

但每当我将函数赋值给变量时,就会出现如下错误:

await page.evaluate: ReferenceError: __name is not defined
    at eval (eval at evaluate (:226:30), <anonymous>:1:16)
    at UtilityScript.evaluate (<anonymous>:228:17)
    at UtilityScript.<anonymous> (<anonymous>:1:44)

我可以通过将函数赋值给window.foo = () => "foo"并调用它来解决问题,但我不明白为何会出现这个错误。使用箭头函数或普通函数没有区别。如果只是定义非可调用变量,在evaluate上下文中可以正常使用。

那么,如何在不将函数赋值给全局对象的情况下,在evaluate中定义函数?为何可调用变量会有这种特殊情况?这一切都在单次evaluate调用中完成,不需要持久化。

如果不赋值,使用匿名函数可以正常工作;定义非可调用变量也正常;赋值给某个对象也正常:

// 匿名函数直接调用
await page.evaluate(() => {
    return ((): string => "foo")();
  });

// 定义非可调用变量
await page.evaluate(() => {
    const x = "foo";
    return x;
  });

// 赋值给window对象
await page.evaluate(() => {
    window.foo = () => "foo";
    return window.foo();
  });

直接在浏览器控制台定义函数显然也可以正常工作:
在浏览器控制台定义命名函数

该问题并非类似问题的重复,因为我想要在evaluate调用内部定义函数,而非外部。我理解代码需要序列化后发送到浏览器,但此处我仅在浏览器内定义一次,无需序列化。该问题的相关回答甚至就是我尝试的做法。

最小复现示例

以下是在我的机器上能稳定触发错误的最小示例:

const { chromium } = await import("@playwright/test");
  const server = await chromium.launchServer();
  const client = await chromium.connect(server.wsEndpoint());
  const browser = await client.newContext.bind(client)();
  const page = await browser.newPage();

  const shouldBeFoo = await page.evaluate(() => {
    const foo = () => "foo";

    return foo();
  });

// ReferenceError: __name is not defined

这段代码即使在这种不必要使用函数的简化示例中,也能可靠触发错误。除此之外我没有执行其他操作。

如果我调试进入Playwright的evaluate调用,会来到这里:

async evaluate(pageFunction, arg) {
    (0, _jsHandle.assertMaxArguments)(arguments.length, 2);
    return await this._mainFrame.evaluate(pageFunction, arg);
  }

此时已经出现异常:调试器中的“pageFunction”参数是这样的函数:()=>{const foo=__name(()=>"foo","foo");return foo()}。此时调用它仍然可以正常工作。

在_mainFrame.evaluate内部,该函数会被序列化,我猜测这是问题所在:

const result = await this._channel.evaluateExpression({
      expression: String(pageFunction),
      isFunction: typeof pageFunction === 'function',
      arg: (0, _jsHandle.serializeArgument)(arg)
    });

这也可能只是Playwright的一个bug。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 07:51:07