Подтвердить что ты не робот

Передача аргументов анонимной функции внутри page.includeJs() и page.evaluate()

Немного фона... Я немного новичок в 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: не удается найти переменную: comment

phantomjs://webpage.evaluate(): 2

phantomjs://webpage.evaluate(): 3

phantomjs://webpage.evaluate(): 3

2: null

Хорошая новость: я знаю, что работает, а что нет, но как насчет небольшой последовательности?

Почему разница между includeJs и evaluate?

Каков правильный способ передачи аргументов анонимной функции?

4b9b3361

Ответ 1

Трудная вещь для понимания с помощью PhantomJS состоит в том, что есть два контекста выполнения - контекст Phantom, который является локальным для вашего компьютера и имеет доступ к объектам phantom и require d, а удаленный контекст, который существует в window браузера без браузера и имеет доступ только к вещам, загруженным в веб-страницы, которые вы загружаете через page.load.

Большая часть script, которую вы пишете, выполняется в контексте 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"
    

Таким образом, правильный способ передачи аргументов in отличается для функций в этих разных методах. Для injectJs обратный вызов будет вызываться с новым набором аргументов (включая, по крайней мере, URL-адрес), поэтому любые переменные, которые вы хотите получить, должны находиться в области охвата обратного вызова (т.е. У вас есть доступ к ним в пределах закрытие функции). Для evaluate есть только один способ передать аргументы, которые должны включать их в аргументы, переданные самому evaluate (есть и другие способы, но они сложны и не стоит обсуждать сейчас, что эта функция имеется в самой PhantomJS).