在事件中更改全局变量无法正常工作

时间:2016-09-12 15:34:50

标签: javascript javascript-events phantomjs

我在普通js中有代码,其中事件处理程序更改全局变量而不传入它。

"use strict";
var outsideEventString ="hello";
console.log("before event " + outsideEventString);

const EventEmitter = require('events');

class MyEmitter extends EventEmitter {}

const myEmitter = new MyEmitter();

/*
  Assume a and b are outside your control, 
  but you still need to set the outsideEventString arg
*/
myEmitter.on('event', function(a, b) {
  console.log(a, b, this);
  console.log("inside event " + outsideEventString);
  outsideEventString="goodbye";
});
myEmitter.emit('event', 'a', 'b');

// this prints out goodbye which is set in the handler only
console.log("after event " + outsideEventString);

将onDOMContentLoaded事件和处理程序添加到phantomjs示例netlog.js后,代码如下所示:

"use strict";
console.log("netlog.js");
var page = require('webpage').create(),
    system = require('system'),
    address;

var valueString = "hello";
var value1 = {
    current: false,
    timeStamp: null
};
if (system.args.length === 1) {
    //console.log('Usage: phantomjs netlog.js <some URL>');
    phantom.exit(1);
} else {
    var PHANTOM_FUNCTION_PREFIX = '/* PHANTOM_FUNCTION */';
    address = system.args[1];
    page.onConsoleMessage = function(msg) {
        if (msg.indexOf(PHANTOM_FUNCTION_PREFIX) === 0) {
            eval('(' + msg + ')()');
        } else {
            console.log(msg);
        }
    };
    page.onInitialized = function() {
        // add handler & apply value1 to handler's scope'
        page.evaluate(function(domContentLoaded) {
            document.addEventListener('DOMContentLoaded', domContentLoaded, false);
        }, page.onDOMContentLoaded.apply(this, value1));
    };
    // handler has access to value1 and valueString
    page.onDOMContentLoaded = function(event) {
            value1.current = true;
            value1.timeStamp = Date.now();
            valueString="goodbye";
            console.log('**** DOM CONTENT LOADED ****');
    };
    page.onResourceReceived = function (res) {
        console.log('received ' + res.id + ' ' + res.stage);
    };
    page.onError = function (msg, trace) {
        console.log(msg);
        trace.forEach(function(item) {
            //console.log('  ', item.file, ':', item.line);
        });
    };
    page.open(address, function (status) {
        console.log("page opened");
        if (status !== 'success') {
            console.log('FAIL to load the address');
        }
        console.log("value = " + JSON.stringify(value1));
        console.log("valueString=" + valueString);
        phantom.exit();
    });
} 

使用代码,valueString是&#39;再见&#39;即使它没有通过&#39; .apply(this,value1)&#39;

传入

输出显示

netlog.js
received 1 start
received 1 end
received 2 end
received 3 start
received 3 end
**** DOM CONTENT LOADED ****
received 5 start
received 5 end
received 6 start
received 6 end
received 7 start
received 9 start
received 8 start
received 7 end
received 9 end
received 8 end
received 4 start
received 4 end
page opened
value = {"current":true,"timeStamp":1473693805039}
valueString=goodbye
**** DOM CONTENT LOADED ****
**** DOM CONTENT LOADED ****

valueString变量设置在右侧url收到的id为3并且它粘在一起,以便我可以在page.open中看到它。

为什么&#39; **** DOM内容加载****&#39;在page.open之后打印?

但是如果我将page.onInitialized更改为以下内容而没有apply,则不会设置value1或valueString。

page.onInitialized = function() {
    // add handler & apply value1 to handler's scope'
    page.evaluate(function(domContentLoaded) {
        document.addEventListener('DOMContentLoaded', domContentLoaded, false);
    }, page.onDOMContentLoaded);
};

为什么我需要使用apply方法来获取在处理程序中设置的全局变量?

为什么我只需将value1传递给apply即可设置valueString?

2 个答案:

答案 0 :(得分:1)

page.evaluate是PhantomJS中页面上下文的大门。只能传入原始对象。来自docs

  

注意: evaluate函数的参数和返回值必须   是一个简单的原始对象。经验法则:如果可以的话   通过JSON序列化,然后就可以了。

     

闭包,函数,DOM节点等将工作!

因此,page.onDOMContentLoaded是一个函数,不能传递给页面上下文。你必须传递和反对页面上下文并在那里定义一个函数。

您可以使用类似的内容(使用the onCallback & callPhantom pair):

page.onCallback = function(data){
    if (data.type == 'DOMContentLoaded') {
        console.log('outer: DOMContentLoaded');
    }
};
page.onInitialized = function() {
    page.evaluate(function(value1) {
        // TODO: do something with value1
        document.addEventListener('DOMContentLoaded', function(){
            console.log('inner: DOMContentLoaded');
            window.callPhantom({ type: 'DOMContentLoaded' });
        }, false);
    }, value1);
};
  

为什么'**** DOM CONTENT LOADED ****'打印在page.open后?

请记住,Function.prototype.apply会立即执行一项功能。在这种情况下,它是page.onDOMContentLoaded内的page.onInitialized函数。因此,无论何时页面“初始化”,您都会看到执行page.onDOMContentLoaded

的结果

为什么在执行停止后初始化两个页面是一个完全不同的问题。这可能只是PhantomJS的一些特点。

答案 1 :(得分:0)

基于Artjom B.答案,这是工作代码:

"use strict";
console.log("netlog.js");
var page = require('webpage').create(),
    system = require('system'),
    address;

var valueString = "hello";
var value1 = {
    current: false,
    timeStamp: null
};
if (system.args.length === 1) {
    //console.log('Usage: phantomjs netlog.js <some URL>');
    phantom.exit(1);
} else {
    var PHANTOM_FUNCTION_PREFIX = '/* PHANTOM_FUNCTION */';
    address = system.args[1];
    page.onConsoleMessage = function(msg) {
        if (msg.indexOf(PHANTOM_FUNCTION_PREFIX) === 0) {
            eval('(' + msg + ')()');
        } else {
            console.log(msg);
        }
    };
    page.onCallback = function(data){
        if (data.type == 'DOMContentLoaded') {
            value1.current = true;
            value1.timeStamp = Date.now();
            valueString = "goodbye";
            console.log('outer: DOMContentLoaded');
        }
    };
    page.onInitialized = function() {
        page.evaluate(function() {
            document.addEventListener('DOMContentLoaded', function(){
                console.log('inner: DOMContentLoaded');
                window.callPhantom({ type: 'DOMContentLoaded' });
            }, false);
        });
    };
    page.onResourceReceived = function (res) {
        console.log('received ' + res.id + ' ' + res.stage);
    };
    page.onError = function (msg, trace) {
        console.log(msg);
        trace.forEach(function(item) {
            //console.log('  ', item.file, ':', item.line);
        });
    };
    page.open(address, function (status) {
        console.log("page opened");
        if (status !== 'success') {
            console.log('FAIL to load the address');
        }
        console.log("value = " + JSON.stringify(value1));
        console.log("valueString=" + valueString);
        phantom.exit();
    });
}

此代码允许onDOMContentLoaded事件处理程序设置事件的时间戳 - 这是一个全局变量。