`this` and scope in Javascript callback - javascript

I will show the code directly:
disable: function(e){
that = this;
var haha = this;
$.post(url, function(){
console.log(this);// (why ajax object here?
console.log(that);// (I understand this one works
console.log(haha);// ReferenceError
})
}
What I get confused here is:
Why this in callback does not refer to the outside one? I think this in callback follow the default binding role.
Why haha does not refer as that do? I think when haha is not found in local scope, it will go to outer scope.
I know using that is not a good way. That's why I tried haha, but it failed.

I think you are trying to access those values from console... in that case haha will not work as it is local to the function, where as you have created that as a global variable(as there is no var used).
But that is a wrong pattern because some other script could modify the value of that before the ajax request is completed.

The answer to question 1 is: Because you can rebind it willy-nilly in Javascript, and jQuery happens to for jQuery.post(), as the documentation for jQuery.ajax() states:
The this reference within all callbacks is the object in the context option passed to $.ajax in the settings; if context is not specified, this is a reference to the Ajax settings themselves.
Generally: you should probably never rely on a Javascript library to not rebind this. If you need its value in a nested callback, just save it. Either in a different-named variable, or using Function.bind():
$(function() {
var self = this;
$.post("/echo/json/", (function() {
console.log("self", self); // this will be the document itself
console.log("this", this); // as will self
console.log("self === this", self === this); // should output true
}).bind(this));
});
Example on jsFiddle: https://jsfiddle.net/millimoose/Lx2oxobg/. For what it's worth, I strongly prefer using a separate variable for readability, because you can give it a descriptive name, and the fact that this isn't rebound, and that you've effectively reassigned one of the parameters of the callback, isn't hidden all the way after the block for which this holds true.
As for your question 2, I can't reproduce it, see my second fiddle: https://jsfiddle.net/millimoose/zL352rzf/. As others have stated, you're probably not actually getting the ReferenceError from the console.log() itself, given your screenshot.

Try using context option of $.ajax() to set this of success , error callbacks
disable: function(e) {
$.ajax({
context: this
, type:"POST"
, url:url
, success: function() {
console.log(this);
}
, error: function() {
console.log(this)
}
})
}

The callback function runs in a different its own scope hence this refers to it, not where it was defined. you can use bind or apply or call functions to bind it.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
so this refers to something else, but since that is declared in the scope that the function was declared in it still exists there.

Related

Call functions within event handler

I have written two functions in JavaScript code as follows
Manager = FormManager.extend({
First: function () {
var response = this.Second("Feature"); //I'm able to get the alert
//I have added a click event handler
$('#element').on('click', function(){
var newResponse = this.Second("Bug"); //The alert is not poping
});
}
Second: function (type) {
alert(type);
//Performs certain operation
}
});
Error: Uncaught TypeError: Object #<HTMLButtonElement> has no method 'Second'
I also tried without using this keyword like:
Second("Bug") // Error: There is no method
Whereas this a simplified format (in-order to show a simple example) on my program that I'm playing with. I'm struggling to find out the reason.
Can someone direct me to the right path?
You are using incorrect this. try this way. this inside the handler represents #element not the context of the function itself.
var self = this; //cache the context here
$('#element').on('click', function(){
var newResponse = self.Second("Bug"); //Access it with self
});
Also i think you are missing a comma after First function definision and before Second function.
Fiddle
The reason being the callback you give gets invoked from within the context of the element so your this context changes. this context refers to the context from where the callback was invoked. But there are other ways to get around this like using $.proxy while binding your callback with jquery, using EcmaScript5 Function.prototype.bind etc. But ideally you don't want to do that because most of the cases you would need the context of the element there inside the handler.
Every time you use the this context variable in a function you have to consider what its value is.
Specifically that value will be whatever value the caller specified, whether by using myObj.mymethod(...), or mymethod.call(myObj, ...), or mymethod.apply(myObj, [ ... ]).
When your anonymous function $('#element').on('click', ...) is invoked jQuery will set the context to the HTML DOM element - it's no longer referring to your object.
The simplest work around is to obtain a copy of this outside of the callback, and then refer to that copy inside the closure, i.e.:
var that = this;
$('#element').on('click', function() {
// use that instead of this, here
console.log(this); // #element
console.log(that); // your object
});
Another method is using Function.prototype.bind:
$('#element').on('click', (function() {
console.log(this); // your object
}).bind(this));
or with jQuery you can use $.proxy for the same effect, since .bind is an ES5 function.
I actually prefer the var that = this method, since it doesn't break the jQuery convention that this refers to the element associated with the event.

In javascript how can I call one prototype method in another prototype method?

suppose I have a function:
function test(){}
test.prototype.method01=function(){
//do something
}
test.prototype.method02=function(){
//how can I call the method01?
//this.method01()...?
//but the chrome through an error:
//Uncaught TypeError: Object #<HTMLImageElement> has no method 'method01'
}
Edited:
in fact the method01 like this:
test.prototype.method02=function(){
$('.cpy').resizable({
}).draggable({
start:function(e,ui){
this.method01();
}
});
}
test.prototype.method02=function(){
var testThing = this;
$('.cpy').resizable({
}).draggable({
start:function(e,ui){
testThing.method01();
}
});
}
You have to preserve the this reference in another local variable so that the callback function can use it when calling the other method. The this reference is bound upon each and every function call, including calls to callback functions like the one you're using in the ".draggable()" setup. When that's called this will be set to something different from the this in your "method02" function.
Yea, you could manually cache this in the lexical scope like other answers in this question suggest. However, the alternative that i would suggest is to create a bound method using $.proxy or function.bind as your call back.
Bound methods are always called with a stable this. I find them to be much more readable, than bizarrely named references to this in higher scopes
whats about
test.prototype.method02=function(){
this.method01.apply(this);
// do some other stuff
}

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.logToConsole("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.

Decoupling when using anonymous functions in third party javascript (FB)

I'm using the FB.Event.subscribe() observer model to find out when a user logs in. This method takes two arguments, a string containing the thing to watch, and callback function.
I'm following several events that handle the event the same way, so I've set up the callback function as a pre defined method and passed this to FB.Event.subscribe() like this:
Controller.prototype.go = function() {
FB.Event.subscribe('auth.login', this.fbHandleStatusChange);
FB.Event.subscribe('auth.logout', this.fbHandleStatusChange);
}
Controller.prototype.fbHandleStatusChange = function(response) {
// Doesn't work
this.otherFunction();
}
Controller.prototype.otherFunction = function() {
alert('hello');
}
Unfortunately this means that I loose access to 'this' within the scope of fbHandleStatusChange, obviously I don't want to start coding references to concrete versions of Controller!
I'm guessing I'm passing the function incorrectly?
Thanks.
In JavaScript, this is defined entirely by how a function is called, not where it's defined. This is different than some other languages. (JavaScript doesn't have methods, it just has functions and some syntactic sugar that makes them look like methods sometimes.) So although you're passing in your function correctly, Facebook doesn't know about your object instance and can't set this correctly when calling your function.
Check the FB.Event.subscribe docs to see if it offers a way to say what "context" to use to call the event handler function. It may offer a way to do that. (This will usually be a context or thisArg parameter.)
If not, you can readily solve the problem with a closure:
Controller.prototype.go = function() {
var self = this;
FB.Event.subscribe('auth.login', handleChange);
FB.Event.subscribe('auth.logout', handleChange);
function handleChange() {
return self.fbHandleStatusChange();
}
}
That grabs a copy of this into a variable called self, which is used by the handleChange function (which is a closure over the scope containing the self variable) to call your function with the correct context. More about closures here: Closures are not complicated More about this here: You must remember this
Alternately, though, are you really going to have multiple instances of Controller? People coming to JavaScript from class-based languages tend to use constructor functions (a rough "class" analogue) unnecessarily. They're the right choice if you need to have more than one instance of an object, but if you're only ever going to have a single Controller object on the page, then using a constructor function and fiddling about with this is overkill.
If you don't need multiple, independent Controller instances, then:
var controllerObject = (function() {
var inst = {};
inst.go = go; // Make `go` a publicly-accessible function of the object
function go() {
FB.Event.subscribe('auth.login', fbHandleStatusChange);
FB.Event.subscribe('auth.logout', fbHandleStatusChange);
}
// This is private to us, so we don't expose it as a property on the object
function fbHandleStatusChange(response) {
// Doesn't work
otherFunction();
}
// This is also private to us
function otherFunction() {
alert('hello');
}
return inst;
})();
That creates a private scope via the outer anonymous function, and within that scope creates an instance (inst) which we then return and refer to as controllerObject. controllerObject in the above only has one property, the function go. All of our other functions are truly private. (I've also taken the liberty of ensuring that the functions have names, because that helps your tools help you.)
Note that we don't actually refer to inst anywhere in our function calls, because they're all local to the closure scope. We can even have private data, by having other vars within the outer closure.

Expecting the right calling context (this) in the JavaScript object

Consider this:
window.onload = function () {
myObj.init();
};
var myObj = {
init: function () {
console.log("init: Let's call the callMe method...");
//callMe is not defined...
callMe();
//Works fine!
this.callMe();
},
callMe: function () {
console.log('callMe');
}
};
Since the init function gets called this way (myObj.init), I expect this to be myObj in the init function. And if that is the case, why the callMe function fails? How am I supposed to call the callMe function without using the this context in the init body? (Actually, it's too annoying to call the object methods using this over and over again through the functions. So what's the point of having a single object?)
I would like to know how can I fix this so that the callMe method gets called using the first invocation in the code above?
this is never implicit in JavaScript as it is in some other languages. Although there are ways to do it, like this using the with statement:
init: function () {
console.log("init: Let's call the callMe method...");
// Make `this` implicit (SEE BELOW, not recommended)
with (this) {
// Works
callMe();
}
},
...it's generally a bad idea. Douglas Crockford probably wrote one of the better descriptions of why it's a bad idea, which you can find here. Basically, using with makes it nearly impossible to tell what the code's going to do (and slows the code down, if you do anything else in that with statement that doesn't come from the this object).
This isn't the only way that JavaScript's this is not the same as it is in some other languages. In JavaScript, this is defined entirely by how a function is called, not where the function is defined. When you do this.callMe() (or the equivalent this["callMe"](), or of course foo.callMe(), etc.), two things happen: The function reference is retrieved from the property, and the function is called in a special way to set this to be the object that property came from. If you don't call a function through a property that way, the call doesn't set any particular this value and you get the default (which is the global object; window on browsers). It's the act of making the call that sets what this is. I've explored this in depth in a couple of articles on my blog, here and here.
This (no pun) can be made even clearer if you look at JavaScript's call and apply functions, which are available on all function objects. If I do this:
callMe.call({});
...it'll call the callMe function with a blank object ({}) as this.
So basically, just get used to typing this. :-) It's still useful to have properties and methods associated with an object, even without the syntactic convenience (and confusion!) of an implicit this.
You can also use the module pattern, which captures all private variables inside a closure, so you are free to use them without this, as they're in the same scope. You then pick and choose which methods/variables you want to make public:
var myObj = (function () {
var init = function () {
callMe(); // This now works
};
var callMe = function () {
...
};
// Now choose your public methods (they can even be renamed):
return {
init: init, // Same name
callMyName: callMe // Different name
};
}) ();
Now:
myObj.init(); // Works
myObj.callMyName(); // Works
myObj.callMe(); // Error

Categories