I have an object in a javascript. A method in the object is called from outside the object. I want this method to call itself (kind of recursion but not quite) after 200ms untill a condition is fullfilled.
this.setSearchResult = function(data){
if(initiated){
doSetSearchResult(data);
}else{
console.log("Map is not initiated yet. Waiting 200 ms");
setTimeout(function(){setSearchResult(data);}, 200); // <- Error
}
}
Calling setSearchResult is done this way:
mapView = new MapView(imageData);
mapView.setSearchResult(data);
The error I get is ReferenceError: setSearchResult is not defined.
One way to overcome the error is by changing the setTimeout call to this:
setTimeout(function(){mapView.setSearchResult(data);}, 200);
But I find that rather ugly (even though it would probably work in my application).
Is there a proper way of doing it? Do I have to skip using setTimeout in setSearchResult?
I think this should work:
this.setSearchResult = function(data){
if(initiated){
doSetSearchResult(data);
}else{
var _this = this;
console.log("Map is not initiated yet. Waiting 200 ms");
setTimeout(function(){_this.setSearchResult(data);}, 200); // <- Error
}
}
It is because you are in a callback function so you do not have access to the setSearchResult function.
setSearchResult is a method of your MapView object. You therefore have to call it as such. Inside the method itself, you can use the keyword this to reference the object the method belongs to.
Using this.setSearchResult would work if you would use it directly inside your method. But using it inside your setTimeout callback function, it would reference the window object, because setTimeout is a method of window. To work around this, store this in another variable and use it to point to the right object.
// Store this in a variable called self
var self = this;
setTimeout(function() {
// Inside this function "this" points to window, because setTimeout
// is a method of window. Use the previously declared self instead.
self.setSearchResult(data);
}, 200);
Related
I'm trying to set a field's value to a function, then execute it. this.fetchLocalStorage is not a function is what I get from running it.
var app = {
busdata: (function(){return this.fetchLocalStorage()})(),
fetchLocalStorage: function() {
//fetching
return "fetching data...";
}
};
console.log(app.busdata);
Note that by not making it a self-executing function, it works, but then it would mean the function is called everytime when I only need to fetch the data one time.
busdata: function(){return this.fetchLocalStorage()}
/* ... */
console.log(app.busdata()); //this calls the function every time :(
Thought it might a context problem so I tried a couple things with bind and call but with no luck.
Am I missing something?
this is only bound to the object when you call a method of the object, i.e. app.someMethod(). But you're trying to call fetchLocalStorage() when you're creating the object, not in a method of the object, so this is whatever the outer context is, which is likely the global window object.
You can't refer to other properties of the object until after the object has been created. So just call the function normally after you create the object.
var app = {
fetchLocalStorage: function() {
//fetching
return "fetching data...";
}
};
app.busdata = app.fetchLocalStorage();
I think your params were on the wrong side of the brace.
busdata: (function(){return this.fetchLocalStorage()}() ),
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 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.
I'm trying to setup a function that stores a callback which can be fired later on an event, but I'm not getting the context to bind right. The setup is basically like this:
app.myObject = function(){
this.bindEvents();
};
app.myObject.prototype = {
bindEvents: function(){
var self = this;
this.library = new OutsideLibrary();
this.library.onMyEvent = function(data) {
OtherLibrary.processData(data, self.runCallback); // I'm not able to pass a context here...
}
},
sendToLibrary: function(message,callback) {
this.callback = callback;
this.library.send(message);
}
// ... and this doesn't work because it gets evaluated in the wrong context.
runCallback: function() {
if (this.callback) {
this.callback();
this.callback = null;
}
}
}
I hope the above code makes sense. The idea is, I'm trying to expose an API where I can call:
app.myObject.sendToLibrary("something", function() {...});
...and the callback function will run after the whole chain is complete.
The problem is, I can't pass the callback that is received in sendToLibrary directly through, because the callback need to be passed to another object when an event fires. Also since I'm having to store the currently passed callback, and the callback is optional, I need to unset it once it has fired, so I actually need to pass the runCallback function to run the callback once then unset it.
Anyway, the problem is that when runCallback eventually gets executed it is no longer in the correct context, so it doesn't work. My first idea for how to fix this was to change the runCallback function to include a context argument, so I could pass self.runCallback(self) -- but that executes immediately so it defeats the purpose.
Anyway, I'm sort of lost in the callback woods here, and would appreciate some help finding my way out :)
My question is: is there a way to wrap up a callback function and the correct context, without executing it now, so that when it eventually fires it does so from the correct context?
Thanks!
OtherLibrary.processData(data, function() {
self.runCallback();
)};
Then inside of runCallback this refers to an instance of app.myObject, if this is what you want.
Andrew, I think you might be trying a little too hard, here.
If all you're looking to do is bind context, you can do one of the following:
var working_method = context.method.bind(context);
// bind is IE9+, and does something similar to the below
// or, from whatever scope your context/method are available in, define this function
var working_method = function () { context.method(); };
If you require parameters, and you already know the footprint:
var working_method = function (a, b, c) { context.method(a, b, c); };
The solution for variable argument lengths (ie: a generalized "bind" method) is longer, but if you're defining stuff in line, what I've got here might be all you need.
Just make sure that you do not use this in your wrapping function (whether you save it to a var, or pass it directly into another function), because it will reference window.
Think I been staring at this to long but, I'm trying to bring an object outside the scope of the fetch success method when it completes the fetch.
cars.fetch().complete(function(){
newSuggested = cars.models.filter(function (model) {
return _.contains(model.attributes.suggestedTo, storedVin)
});
})
console.log(newSuggested) //undefined
How can I get the newSuggested outside the fetch scope after it successfully fetched?
Unless you have declared newSuggested somewhere above in the code, it is a global variable on the window (this is not the problem, just pointing it out).
The reason it is undefined where you are logging it, is because when that console.log statement is run, the fetch has not completed.
Whatever you are going to do with newSuggested, you need to do it from within the complete callback function.
// declare the variable using var, so it is not global
var newSuggested;
cars.fetch().complete(function(){
newSuggested = cars.models.filter(function (model) {
return _.contains(model.attributes.suggestedTo, storedVin)
});
console.log(newSuggested); // works!
// do something with newSuggested here, hard to tell what you are trying to do.
probablyUpdateViewInSomeWay(newSuggested);
});
// fetch not complete here!
// this is not a scope problem, but an async problem.
// complete callback has not been called yet.
console.log(newSuggested) //undefined, this is expected
Side note: complete is deprecated in jQuery 1.8, so you should use done instead.
Your script is correct, you can even explicitly use window.newSuggested to make the variable global (though is default like this).
You have to move the console.log after "complete" as call order in the execution flow
cars.fetch().complete(function(){
window.newSuggested = cars.models.filter(function (model) {
return _.contains(model.attributes.suggestedTo, storedVin)
});
global_log();
})
function global_log(){console.log(newSuggested);};