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.
Related
I'm new to JS and especially to prototypes.
I have this class and I cannot figure out how to access the properties.
var Lobby = function (preloader, serverConn) {
// Hold a reference to EventBus
this.serverConn = serverConn;
this.preloader = preloader;
this.scheduleItemService = new ScheduledItemService(this.preloader);
this.stage = new createjs.Stage("lobbyCanvas");
};
Lobby.prototype.start = function(me, signedRequest) {
sendMessage(data, function() {
// inside this scope this.stage is undefined!
renderLobbyImages(this.stage, this.scheduleItemService);
});
};
function renderLobbyImages(stage, scheduleItemService) {
stage.update();
};
Calling code:
var lobby = new Lobby(preloader, serverConn);
lobby.start(me, status.authResponse.signedRequest);
What am I doing wrong accessing 'renderLobbyImages' ??
Thank you :-)
In javascript, this is not resolved based on where it is declared/used. It is resolved when it gets called. (see: How does the "this" keyword in Javascript act within an object literal?).
Therefore, in the code above, since this is called in the callback to sendMessage(), and since sendMessage is asynchronous (meaning the callback will be called long after the call to start() have returned), this is therefore referring to the global object (which is window in web browsers, something unnamed in node.js).
So effectively, your code is doing this (no pun intended):
sendMessage(data, function() {
renderLobbyImages(stage, scheduleItemService);
});
Since there are no global variables called stage or scheduleItemService both are effectively undefined!
Fortunately, there is a workaround for this. You can capture the correct object in a closure:
var foo = this;
sendMessage(data, function() {
renderLobbyImages(foo.stage, foo.scheduleItemService);
});
Alternatively, you can pass the correct object (this) into an IIFE:
(function(x){
sendMessage(data, function() {
renderLobbyImages(x.stage, x.scheduleItemService);
});
})(this); // <-------- this is how we pass this
or:
sendMessage(data, (function(a){
return function(){
renderLobbyImages(a.stage, a.scheduleItemService);
}
})(this));
Or in this case, since stage and scheduleItemService are not functions, you can even pass them directly:
sendMessage(data, (function(a,b){
return function(){
renderLobbyImages(a,b);
}
})(this.stage, this.scheduleItemService));
There are lots of solutions to this problem. Just use the one you're most comfortable with.
Two problems.
this is missing in your constructor function on scheduleItemService.
Some functions you call to assign values seem to be not returning anything.
new createjs.Stage("lobbyCanvas");
new ScheduledItemService
Your calling method is alright.
this always refers to the calling object. When you say...
varlobby = new Lobby();
lobby.start();
... your calling object is lobby which has all the fields the start() function needs. But there initialization seems to be not working properly.
Please read this MDN starter guide.
Also we are having a some discussion about classical and prototype based OOP in this question. Please see the answer of Paul S for more about the tutorial I mentioned. Please see my answer if you need to see the tutorial in classical OOP light.
function validateHistoryId(input) {
$.getJSON('request.php', {"action": "historyExists", "history_id" : input.value},
function(data) {
console.log(input.value);
}
);
}
i use jQuery ajax call from javascript function. I tried the above code and it works, but I don't know will it cause any problems in the future.
I want to know, can ajax callback function see variables of it's parent function? and is it a bad practice to do so?
This is the JavaScript functional way to doing things. It's called closure: functions carry variable pointers from their current scope and from any other parent scope.
So it's not only good practice, but this is the pattern you should normally follow instead of pushing around parameter objects, etc.
Please notice that the "this" reference is special, it is never closured (as opposed to any other references) and thus always point the global object in anonymous functions.
In fact a developer needs some time to fully employ the power of the closure feature - this is a basic example you just written. In more advanced (and not solely async) scenarios closure helps you to create "fire and forget" behavior, or can provide you with "private" variables that are not accessible from client code (useful for library development). Closure also help you isolate your code so that it will not mess with the global scope.
1) Example: how to create protected variables with closures. You are able to acccess two methods that can access "protectedVariable" but you are not able to access it yourself - so the control is guaranteed.
function protectedScope(initData) {
var protectedVariable = initData;
return {
getter: function() { return protectedVariable; }
setter: function(v) { protectedVariable = v; }
}
}
var methods = protectedScope(10);
console.log(methods.getter());
2) isolation: the following code will not garbage the global scope even with "global" function definitions
var API = (function() {
var protectedVariable = 0;
var myNotGlobalFunction() {
return protectedVariable;
}
return myNotGlobalFunction;
})();
For a particular listener in my application, I'm using the following code for scope-busting purposes:
// this is all in a prototype of MyClass
var self = this;
myElement.addEventListener("stuff", function(e){self.doStuff(e)});
That will get doStuff to have the desired this binding.
The problem shows up when I try to removeEventListener. I suppose it's because the native function signatures must be different?
// in a different prototype of MyClass
var self = this;
myElement.removeEventListener("stuff", function(e){self.doStuff(e)}); // doesn't work
If I make a separate function that contains all of my scope-busting code, then the this binding in that code will be to the unwanted object of myElement. So the question is: How can I force listener scope and still be able to remove an added event listener?
*note using global / static variables in any way is prohibited due to the nature of the project (otherwise this would be simple!)
This has nothing to do with scope or the way in which you're storing a reference to this. The problem is that removeEventListener expects a reference to a function that's previously been registered as a listener, but you're giving it a brand new function it's never seen before.
You need to do something like this:
var self = this;
var listener = function(e){self.doStuff(e)}
myElement.addEventListener("stuff", listener);
// later
myElement.removeEventListener("stuff", listener);
It doesn't matter that the bodies of your two functions are the same; they're still different functions.
See:
https://developer.mozilla.org/en/DOM/element.removeEventListener
Inline anonymous functions are a very bad practice anyway, so I will suggest the obvious:
function MyClass() {
this.onStuff = this.onStuff.bind(this); //Each instance steals the prototyped function and adds a bound version as their ownProperty
}
MyClass.prototype = {
onStuff: function (e) { //Prototyped, no instance actually uses this very function
this.dostuff()
},
bind: function () {
myElement.addEventListener("stuff", this.onStuff);
},
unbind: function () {
myElement.removeEventListener("stuff", this.onStuff);
}
}
see removeEventListener on anonymous functions in JavaScript
You can't removeEventListener as your using an anonymous function.
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
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.