I'm attempting to have a javascript object run a deferred method and when it's .done() call a function in that same object. I'm having issues because "this" becomes the deferred object instead of the object that called it.
PageObject.prototype.successFunction = function() {
console.log(arguments);
return console.log(this.name + " Success function called");
};
PageObject.prototype.loadPage = function(url) {
return $.when($.mobile.loadPage("pages/" + url))
.done(this.successFunction);
};
var pg = new PageObject();
pg.loadPage("test.html");
How do I send "this" into the successFunction? This PageObject is going to be extended by others as well, so knowing "this" when running successFunction will be very handy.
It seems simple, and probably has a simple answer. I was looking into .apply() but I'm not sure it helped. This post on stack overflow was helpful a little bit, but it broke the minute I put it into the .done() function.
Functions as parameters (with parameters) -- JavaScript
jQuery's proxy will return a function bound to a specific context:
PageObject.prototype.loadPage = function(url) {
return $.when($.mobile.loadPage("pages/" + url))
.done($.proxy(this.successFunction, this));
};
There is also ES5's bind which operates similarly but needs to be shimmed in older browsers
PageObject.prototype.loadPage = function(url) {
return $.when($.mobile.loadPage("pages/" + url))
.done(this.successFunction.bind(this));
};
apply and call can execute a function immediately with the specified context, but proxy and bind return a function that can be used later or passed to other functions.
The this "variable" in JavaScript is called the "calling object" and is determined when its enclosing function is called (not when it's defined). It can be set a few different ways:
You can set it by using the . operator (e.g. myObject.myFunction())
You can set it by using a function's call() or apply() methods
You can (under ES5) bind it permanently by using the function's bind() method
It will default to the global object if none of the above methods set it to something else
The important thing to understand is that (aside from method 3 above), it's all about how the function is called when it's called, not how it's referenced, not how it's stored, not where it's created.
In your case, you're passing this.successFunction as a function reference to be called by jQuery, but it doesn't call it that way (because it just got a reference to the function, not any information on how that function should be called).
There are a few fancy tricks you can use with jQuery's $.proxy() or ES5's .bind() methods, but when it comes down to it, the most straightforward way to handle it is to simply retain this through a closure-scoped variable and use a function wrapper:
PageObject.prototype.loadPage = function(url) {
var self = this;
return $.when($.mobile.loadPage("pages/" + url))
.done(function () { self.successFunction(); });
};
Note that we're using method 1 to explicitly bind successFunction's calling object to be the same as loadPage's calling object. It's short, simple, clear, works fine under ES3, and doesn't depend on jQuery.
Related
I have a function, functionWithDifferentScope, that takes an object, myobject.options, as a parameter. One of the pairs in the options object is a callback which points to a function defined in myObject: myCallback.
What I'm trying to achieve is injection of the myObject namespace into the callback of a function that is defined (by a 3rd party) at the global level.
A simplified example:
var myObject = {
options: {
callback: this.myCallback(this),
...,
},
init: function() {
// functionWithDifferentScope operates in the 'window' context
functionWithDifferentScope(this.options);
},
myCallback: function(namespace) {
// 'this' is window
// 'namespace' is myObject
}
}
myObject.init();
When executing this script, this.myCallback(this) appears to be executed at definition (due to the parenthesis?); as well as once myObject.init(); is caled. During the first executions this is myObject, but subsequent calls through the functionWithDifferentScope identify this as window.
Is there a way to pass the myObject namespace to the myObject.options.callback value as a parameter?
Do you mean this?
var myObject = new (function() {
var t = this;
vac callback = function() {
// t equals to the myObject-instance
// this equals to window
}
this.init = function() {
funcWithDifferencScope(callback);
}
})();
myObject.init();
I think what you are looking for is prototype style "bind"
Basically "this.myCallback(this)" is a call to the function.
this.myCallback is the function itself. (It is an object with the type function).
You can call it using the method 'call' or 'apply' that you can use on functions. Which will call these functions.
See:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/call
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply?redirectlocale=en-US&redirectslug=JavaScript%2FReference%2FGlobal_Objects%2FFunction%2Fapply
The first argument is the object context to work in. What I think you mean by object namespace.
so: a.callback(5) is the same as a.callback.call(a,5)
However please note that these days if you are working with most javascript libraries you probably have a 'bind' function that will do the work for you.
http://prototypejs.org/doc/latest/language/Function/prototype/bind/
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
the idea is that this.callback.bind(this) returns a Function object you can call that will inject the correct context automatically so you can pass the return value of bind alone as a callback and be assured that the method will be executed on the correct object.
I am study the udacity's course and encounter a problem.
https://www.udacity.com/course/viewer#!/c-cs255/l-49464373/e-73862317/m-73162952
function xhrGet(reqUri,callback) {
var xhr = new XMLHttpRequest();
xhr.open("GET", reqUri, true);
xhr.onload = callback;
xhr.send();
}
var TILEDMapClass = Class.extend({
// Boolean flag we set once our map atlas
// has finished loading.
fullyLoaded: false,
//-----------------------------------------
// Load the json file at the url 'map' into
// memory. This is similar to the requests
// we've done in the past using
// XMLHttpRequests.
load: function (map) {
// Perform an XMLHttpRequest to grab the
// JSON file at url 'map'. We've provided
// the xhrGet function from the optional
// unit for you to use if you want.
//
// Once the XMLHttpRequest loads, set the
// 'fullyLoaded' flag to true.
//
// YOUR CODE HERE
xhrGet(map, function(){
this.fullyLoaded = true;
});
}
});
// We define a single global instance of our
// map for the rest of our game code to access.
var gMap = new TILEDMapClass();
the link says that it use gMap.load.apply(gMap, [jsonURL]);
http://forums.udacity.com/questions/100058023/scope-of-this#cs255
but I think that inspite the fact using the called mothod.(The load will belong to gMap)
But because
xhr.onload = function(){
this.fullyLoaded = true;
}
is a method belong to the xhr object,
and the this is inside an an anonymous function
the this should reference the xhr not gMap.
Why the this reference gMap?
this is funny within closures. You have to remember that the this keyword will usually refer to the owner of the method. Usually the caller (window for global functions) but when a method is called as a property of an object, this will refer to the object itself.
See this: "this refers to the parent object inside function code if the function is called as a property of the parent." Understanding this
The rules directly from Understanding this:
By default, this refers to the global object.
When a function is called as a property on a parent object, this
refers to the parent object inside that function.
When a function is called with the new operator, this refers to the
newly created object inside that function.
When a function is called using call or apply, this refers to the
first argument passed to call or apply. If the first argument is null
or not an object, this refers to the global object.
this doesn't necessarily mean the function or object it's being called on, if you're used to using jQuery and are confused by this, the jQuery methods actually set this on all of its functions for convenience by calling one of these two functions which set this to the caller:
call(object, arg0, arg1...);
apply(object, args[]);
So basically, unless the function is setting this by calling one of the above functions, it will be set to some outer function/object or window.
"this" in a javascript function has nothing to do with the object to which the function belongs, but what object it is executed against
Contrast with Java, where those are the same because a method is truly part of an object and cannot exist without one (not considering statics).
For example:
var blah = {
test: function () {
console.log('test');
}
};
var f = blah.test;
var bleh = {
test: blah.test
}
If I then make each of these three function calls, what is "this" pointing to in each call?
blah.test(); // this points to blah
f(); // this is null (or undefined, not sure which)
bleh.test(); // this is bleh
I can also use Function.call to call a function object in the context of any object: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/call
f.call(window); // this is window
Understanding "this" is difficult when working with callbacks because the callback function is usually invoked by some other library (like jquery for instance) and their API may or may not make a guarantee to what "this" refers to. What you can do as a work-around:
someAsyncFunction(function () {
bleh.test();
});
That will ensure the function you care about is called with a predictable "this" reference.
I am trying to create a JavaScript object as follows.
var MyObject = function (parameters) {
this.parameters = parameters;
parameters.userFunction(this.MyObjectCallback);
}
MyObject.SOME_STATIC_VARIABLE = 21;
MyObject.prototype = {
myObjectCallback: function() {
console.log(this);
}
}
The MyObject object will accept a userFunction to which it will pass a handler. The user function will do some logic and pass the result back to the instance, for example:
new MyObject({userFunction: function(callback) {
$.post(
'http://localhost/~knyttl/source.php',
{},
callback,
'json');,
}});
Unfortunately, even though the callback is properly called, this gets an instance of the JQuery object and not of the MyObject instance as I would like. To conclude, I can not manage to keep the MyObject instance.
I am not even sure, whether this is a correct way of creating JavaScript objects. I will be grateful for any suggestion.
You can bind a specific this value using .bind. Also I corrected the capitalizing of My.
parameters.userFunction(this.myObjectCallback.bind(this));
When you call a function like a.b(), then inside b, this === a. However, if you do not directly call it but only pass the function (like a.b) and call it later, this binding is lost.
.bind returns a new function which now receives the jQuery ajax result as this. However, it ignores that and calls myObjectCallback with the predefined (bound) this.
.bind is not available on older browsers but there are shims available.
See apply() and call().
parameters.userFunction.call(this, this.MyObjectCallback);
jQuery allows you to set the context of your callback.
You are in a weird situation where you design has hurt you. Your MyObject can't be passed in as the context, because it is being created at the same time.
new MyObject({userFunction: function(callback) {
$.post(
'http://localhost/~knyttl/source.php',
{},
callback,
'json');,
}});
So instead:
var myObj = new MyObejct();
myObj.setCallback({userFunction: function (callback) {
$.ajax({
context: myObj,
url: 'http://localhost/what ever /',
success: callback,
dataType: 'json',
data: {}
}
});
Try this.
var myObject = {
obj1:function(paremeters){
this.Name = paremeters
return this},
};
I would suggest you read,
http://javascript.crockford.com/javascript.html
Using this in javascript has the potential to be very confusing. It is assigned the value of whatever is behind the dot when the function is called e.g.
window.doSomething();
will cause this to be set to window, whereas
myDOMElement.onClick();
will cause this to be set to myDOMElement.
In your case, it comes down to JQuery's internal workings, so have a look at this very thorough set of explanations to get an understanding of how JQuery handles this.
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.
this thing almost works:
function myClass(url) {
this.source = url;
this.rq = null;
this.someOtherProperty = "hello";
// open connection to the ajax server
this.start = function() {
if (window.XMLHttpRequest) {
this.rq = new XMLHttpRequest();
if (this.rq.overrideMimeType)
this.rq.overrideMimeType("text/xml");
} else
this.rq = new ActiveXObject("Microsoft.XMLHTTP");
try {
this.rq.onreadystatechange = connectionEvent;
this.rq.open("GET", this.source, true);
this.rq.send(null);
this.state = 1;
} catch (err) {
// some error handler here
}
}
function connectionEvent() {
alert("i'm here");
alert("this doesnt work: " + this.someOtherProperty);
}
} // myClass
so it's nothing more than having the XMLHttpRequest object as a member of my class, instead of globally defined, and invoking it in the traditional way. however, inside my connectionEvent callback function, the meaning of "this" is lost, even though the function itself is scoped inside myClass. i also made sure that the object that i instantiate from myClass is kept alive long enough (declared global in the script).
in all the examples of using javascript classes that i saw, "this" was still available inside the inner functions. for me, it is not, even if i take my function outside and make it a myClass.prototype.connectionEvent. what am i doing wrong? thank you.
The reason it's not working is that in Javascript, this is defined entirely by how a function is called, not where it's defined. This is different than some other languages.
To have this mean what you expect, you'd have to ensure that explicitly by "binding" it:
this.start = function() {
var self = this; // Set up something that survives into the closure
/* ...lots of stuff omitted... */
this.rq.onreadystatechange = function() {
// Call `connectionEvent`, setting `self` as `this` within the call
connnectionEvent.call(self);
};
There's more information about this management in this blog post, but basically: When a function is called without any particular effort made to set this, this within the function will always be the global object (window, on browsers). There are two ways to set this when making a call:
Using Function#call (or Function#apply) as I did above, passing in the object reference to use as this as the first parameter. That calls the function and sets this to whatever you passed in. The difference between #call and #apply is how you supply further arguments to pass into the function. With #call you supply them as further arguments to the #call call (e.g. func.call(thisArg, arg0, arg1, arg2)), whereas with #apply you supply them as an array in the second argument (func.apply(thisArg, [arg0, arg1, arg2])).
Using dotted notation: If you have an object that has a property with a function assigned to it (like your start property), calling it by using the object instance, a dot, and the property name (this.start() or foo.start(), etc.) will call the function and set this to the object instance within the call. So the dotted notation does two entirely distinct things: Looks up the property and finds a function as its value, and calls the function such that this is set to the object during the call. Literally it's like: var f = obj.func; f.call(obj).
Slightly off-topic, but: Barring a really good reason to, I wouldn't reinvent this wheel. There are lots of libraries out there to simply XHR calls. jQuery, Prototype, Closure, and nearly all the rest.