开发者

javascript OOP Confusion [duplicate]

This question already has answers here: Closed 11 years ago.

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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜