将参数传递给page.includeJs()和page.evaluate()中的匿名函数

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (1)
  • 关注 (0)
  • 查看 (123)

.我对JavaScript有点新,对于phantom.js,我不知道这是一个JavaScript还是phantom.js错误(功能?)。

以下成功完成(对于缺少的phantom.exit(),遗憾的是,一旦完成,您只需按Ctrl + C):

var page = require('webpage').create();
var comment = "Hello World";

page.viewportSize = { width: 800, height: 600 };
page.open("http://www.google.com", function (status) { 
    if (status !== 'success') {
        console.log('Unable to load the address!');
        phantom.exit();
    } else {
        page.includeJs('http://code.jquery.com/jquery-latest.min.js', function() {
            console.log("1: ", comment);
        }, comment);

        var foo = page.evaluate(function() {            
            return arguments[0];
        }, comment);

        console.log("2: ", foo);            
    }
});

这工作:

page.includeJs('http://code.jquery.com/jquery-latest.min.js', function() {
    console.log("1: ", comment);
}, comment);

输出1: Hello World

但不是:

page.includeJs('http://code.jquery.com/jquery-latest.min.js', function(c) {
    console.log("1: ", c);
}, comment);

输出1: http://code.jquery.com/jquery-latest.min.js

并不是:

page.includeJs('http://code.jquery.com/jquery-latest.min.js', function() {
    console.log("1: ", arguments[0]);
}, comment);

输出1: http://code.jquery.com/jquery-latest.min.js

看第二张,这是有效的:

var foo = page.evaluate(function() {            
    return arguments[0];
}, comment);

console.log("2: ", foo);

输出2: Hello World

和这个:

var foo = page.evaluate(function(c) {           
    return c;
}, comment);

console.log("2: ", foo);

输出2: Hello World

但不是这样的:

var foo = page.evaluate(function() {            
    return comment;
}, comment);

console.log("2: ", foo);

输出

ReferenceError:找不到变量:注释 phantomjs://webpage.evaluate():2 phantomjs://webpage.evaluate():3 phantomjs://webpage.evaluate():3 2:null

好消息是,我知道哪些是有效的,哪些没有,但是一致性如何?

为什么之间的差异includeJsevaluate

将参数传递给匿名函数的正确方法是什么?

提问于
用户回答回答于

用PhantomJS理解的棘手问题是,有两种执行上下文 - Phantom上下文,它是本地的,可以访问phantom对象和required模块,以及远程上下文,它存在于window无头浏览器中,只有可以访问通过您加载的网页加载的内容page.load

您编写的大部分脚本都是在Phantom上下文中执行的。主要的例外是其中的任何内容page.evaluate(function() { ... })。在...这里在远程情况下,这是沙箱运行,而无需访问变量和对象在本地范围内。您可以通过以下方式在两个上下文之间移动数据:

  • 从传递给page.evaluate()或的函数返回一个值
  • 将参数传递给该函数。

所传递的值本质上是在每个方向上进行序列化的 - 你不能将一个复杂的对象与方法一起传递,只能传递一个数据对象,比如一个字符串或一个数组(我不知道确切的实现,但经验法则似乎是任何可以用JSON序列化的东西都可以在任何方向传递)。您无法page.evaluate()像使用标准Javascript一样访问函数外部的变量,只能访问您显式作为参数传入的变量。

所以,你的问题:为什么includeJs和评估之间的区别?

  • .includeJs(url, callback)需要一个在Phantom环境中执行的回调函数,显然接收url作为其第一个参数。除了它的参数之外,它还可以访问(就像任何普通的JavaScript函数)其封闭范围内的所有变量,包括comment在您的示例中。它并没有采取额外的参数列表中的回调函数之后-当你引用comment的回调中,引用的是外部的变量,而不是函数的参数。 var foo = "stuff"; page.includeJs('http://code.jquery.com/jquery-latest.min.js', function() { // this callback function executes in the Phantom context console.log("jQuery is loaded in the remote context."); // it has access to outer-scope variables, including "phantom" nowDoMoreStuff(foo, page); });
  • .evaluate(function, args*)需要一个函数来执行并​​传递零个或多个参数(以某种序列化的形式)。您需要在函数签名中命名参数,例如function(a,b,c),或者使用该arguments对象来访问它们 - 它们不会自动具有与您传递的变量相同的名称。 var foo = "stuff"; var bar = "stuff for the remote page"; var result = page.evaluate(function(bar2) { // this function executes in the remote context // it has access to the DOM, remote libraries, and args you pass in $('title').html(bar2); // but not to outer-scope vars return typeof foo + " " + typeof bar; }, bar); console.log(result); // "undefined undefined"

因此,传递参数的正确方法与这些不同方法中的函数不同。因为injectJs回调函数将被调用一组新的参数(至少包括URL),所以你想要访问的变量需要在回调函数的封闭范围中(即你可以在函数的闭包中访问它们) 。因为evaluate只有一种方法可以传递参数,即将它们包含在传递给evaluate自身的参数中(还有其他方法,但它们很棘手,现在不值得讨论这个功能在PhantomJS中可用) 。

扫码关注云+社区

领取腾讯云代金券