javascript OOP Confusion [duplicate]
Possible Duplicate:
Javascript OOP return value from function
I have a class defined like this
function SocialMiner(tabUrl)
{
var verbose=true;
var profileArray=new Array();
this.tabUrl=tabUrl;
this.getTabUrl=function(callback)
{
chrome.tabs.getSelected(null, function(tab)
{
callback(tab.url);
});
}
this.setTabUrlValue=function(pageUrl)
{
this.tabUrl=pageUrl;
console.log("22"+this.tabUrl); //this statement shows url correctly
}
}
When I call this method like these
miner.getTabUrl(miner.setTabUrlValue);
miner.logT开发者_如何转开发oConsole("1"+miner.tabUrl); //This statement returns undefined
The console.log
inside callback correctly outputs url
, however, the tabUrl
property of miner
ojbect is undefined , as seen in second console.log
. Why is it so ?
The solution is to save a reference to this
within the constructor (available later on via closure):
var that = this; //in the top of the SocialMiner constructor function
and in setTabUrlValue
use:
that.tabUrl=pageUrl;
I suspect running a method as a function (callback
) loses scope, i.e. doesn't know of any this
anymore. In other words, it runs within the scope of the constructor, not as a method of the instance using it. A variable referencing this
in the constructor scope is available to the function, and that
points to the right this
on instance creation.
You could also force callback
to run in the current instance scope like this:
callback.call(this,tab.url);
In that case you can leave this.tabUrl=pageUrl;
as it is.
This is an simplification of your code. The methods return this
to be able to directly reference a property of the instance (see console.log
last line):
function Some(){
var that = this; // note: not used in this example
this.getA = function(callback){
someval = 'foobar';
callback.call(this,someval);
return this;
};
this.getB = function(val){
this.val = val;
return this;
};
}
var some = new Some;
console.log( some.getA(some.getB).val ); //=> foobar
Taking a look @ your code again, I think you're loosing scope twice, because callback
is called from within another callback. That's why I think your code on that spot should be:
chrome.tabs.getSelected(
null,
function(tab) {
callback.call(that,tab.url); //< use that here
}
);
Furthermore, in you code @ github, I don't see any instantiation of the miner
instance.
this
is a tricky beast in JavaScript and as others have pointed out is the key to the issue. The problem with using this
everywhere is that it's value can change depending on who/where the function is called from (for example, see the call
and apply
methods in JavaScript). I'm guessing that if you wrote the value of this
to the console in the the callback from the chrome.tabs.getSelected
function you'd find it isn't your miner any more.
The solution is to capture a reference to the this
that you're actually interested in when you know for sure it's the right one & then use that reference from then on. Might make more sense to see it commented in-line in your example:
function SocialMiner(tabUrl)
{
//At this point we know "this" is our miner object, so let's store a
//reference to it in some other (not so transient) variable...
var that = this;
var verbose=true;
var profileArray=new Array();
this.tabUrl=tabUrl;
this.getTabUrl=function(callback)
{
chrome.tabs.getSelected(null, function(tab)
{
//at this point "this" is whatever the "chrome.tabs.getSelected"
//method has decided it is (probably a reference to the tab or something)
callback(tab.url);
});
}
this.setTabUrlValue=function(pageUrl)
{
//because this can be called from anywhere, including the chrome callback
//above, who knows what "this" refers to here (but "that" is definitely
//still your miner)
that.tabUrl=pageUrl;
console.log("22"+that.tabUrl);
}
}
You can see how much this
shifts around in libraries that use callbacks heavily like jQuery, where often this
is set to convenient values, but certainly not the same this
that was logically in scope when you made the initial call.
EDIT: Looking at the full source (& example) you posted, this is just a timing issue where obviously the chrome.tabs.getSelected
is returning asynchronously after your "second" call to log goes through...
console.log("5");
miner.getTabUrl(miner.setTabUrlValue); //setTabUrlValue is logging with '22'
console.log("6");
miner.logToConsole("1"+miner.tabUrl);
console.log("7");
// Output:
5
6
1 undefined //the chrome.tabs.getSelected hasn't returned yet...
7
22 http://url //now it has (so if you tried to use miner.tabUrl now you'd be all good...
The solution is to put all the stuff after the get/set into the callback, since you don't want anything happening until after that tabUrl is finished being set... so something like this:
console.log("5");
miner.getTabUrl(function(pageUrl) {
miner.setTabUrlValue(pageUrl);
console.log("6");
miner.logToConsole("1"+miner.tabUrl);
console.log("7");
});
Hopefully that will see you getting your results in the order you expect them.
I think this happens because closure vars do not survive a function call.
精彩评论