向 puppeteer.Page 对象添加自定义方法

RA.*_*RA. 4 javascript methods extending typescript puppeteer

我想向 puppeteer.Page 对象添加自定义方法,这样我就可以像这样调用它们:

let page = await browser.newPage();
page.myNewCustomMethod();
Run Code Online (Sandbox Code Playgroud)

这是我创建的众多自定义方法之一。它使用表达式数组通过 XPath 表达式找到第一个可用元素:

const findAnyByXPath = async function (page: puppeteer.Page, expressions: string[]) {
    for (const exp of expressions) {
        const elements = await page.$x(exp);

        if (elements.length) {
            return elements[0];
        }
    }

    return null;
}
Run Code Online (Sandbox Code Playgroud)

我必须像这样调用它......

let element = await findAnyByXPath(page, arrayOfExpressions);
Run Code Online (Sandbox Code Playgroud)

对我来说,这在编辑器中看起来很奇怪,尤其是在调用许多自定义方法的区域中。在我看来,有点“断章取义”。所以我宁愿这样调用它:

page.findAnyByXPath(arrayOfExpressions);
Run Code Online (Sandbox Code Playgroud)

我知道有一个page.exposeFunction方法,但这不是我要找的。

有什么方法可以实现这一目标?

Tho*_*orf 6

你能做这个吗?是的。

您可以通过修改其原型来扩展 JavaScript 中的任何对象。为了向Page对象添加函数,您可以Page使用__proto__属性访问对象的原型。

这是一个将函数添加customMethod到所有 Page 对象的简单示例:

const page = await browser.newPage();
page.__proto__.customMethod = async function () {
    // ...
    return 123;
}
console.log(await page.customMethod()); // 123

const anotherPage = await browser.newPage();
console.log(await anotherPage.customMethod()); // 123
Run Code Online (Sandbox Code Playgroud)

请注意,您Page首先需要一个对象来访问原型,因为构造函数(或类)本身并未公开。

你应该这样做吗?不。

您可能已经注意到上面链接的 MDN 文档中的红色警告。仔细阅读它们。一般情况下,不建议更改您正在使用且尚未创建的对象的原型。有人创造了原型,他没想到有人会摆弄它。有关更多信息,请查看此 stackoverflow 问题:

怎么做呢?

相反,您应该只使用自己的函数。拥有自己的函数并用page这样的参数调用它们并没有错:

// simple function
findAnyByXPath(page);

// your own "namespace" with more functionality
myLibrary.findAnyByXPath(page);
myLibrary.anotherCustomFunction(page);
Run Code Online (Sandbox Code Playgroud)

通常,您也可以扩展 class Page,但在这种情况下,库不会导出它class本身。因此,您只能创建一个在内部执行相同功能但在顶部提供更多功能的包装类。但这将是一种非常复杂的方法,在这种情况下确实值得付出努力。