Javascript OOP Confusion

Possible duplicate:
Javascript OOP return value from function

I have a class defined as

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.logToConsole("1"+miner.tabUrl); //This statement returns undefined 

The console.log internal callback correctly displays the url , however the tabUrl miner ojbect undefined property is as shown in the second console.log . Why is this so?

+4
source share
3 answers

this is a complex beast in JavaScript and, as others have pointed out, is the key to the problem. The problem with using this everywhere is that the value may change depending on who / where the function is called (for example, see call and apply in JavaScript). I assume that if you wrote this value in the console in a callback from the chrome.tabs.getSelected function, you will find that it is not your miner.

The solution is to fix the link to this , which you are really interested in, when you know for sure that it is correct, and then use this link from now on. It might make sense to see how he comments on a line in your example:

 function SocialMiner(tabUrl) { //At this point we know "this" is our miner object, so let 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 moves in libraries that use callbacks mainly like jQuery, where this is often set to convenient values, but certainly not the same this that was logically in the area when you made the initial call.

EDIT . Looking at the full source (& example) that you posted , it's just a synchronization issue where, obviously, chrome.tabs.getSelected returns asynchronously after your β€œsecond” call to the 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 data after get / set in the callback, since you do not want something to happen until tabUrl is installed ... 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"); }); 

Let's hope you see that you get the results in the order they expect.

+1
source

The solution is to keep a reference to this inside the constructor (available later through closure):

 var that = this; //in the top of the SocialMiner constructor function 

and in setTabUrlValue use:

 that.tabUrl=pageUrl; 

I suspect that starting a method as a function ( callback ) loses its scope, i.e. does not know any this . In other words, it runs as part of the constructor, and not as an instance method using it. A variable referencing this in the constructor area is available to the function, and that indicates the right of this to instantiate.

You can also force a callback in the current region of the instance as follows:

 callback.call(this,tab.url); 

In this case, you can leave this.tabUrl=pageUrl; as it is.

This is a simplification of your code. Methods return this to be able to directly refer to the instance property (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 

Looking at your @ code again, I think you are losing the area twice because callback is being called from another callback. This is why I think your code at this point should be:

 chrome.tabs.getSelected( null, function(tab) { callback.call(that,tab.url); //< use that here } ); 

Also, in your @github code, I don't see any instance of the miner instance.

+2
source

I think this is because closing the vars could not stand the function call.

+1
source

All Articles