I have an event that binds a function to a click. The click calls another function in the same view. Unfortunately, the scope is not the correct scope. When I try to do this.otherFunction(), the function that is assigned to the click is not in the same scope as this.otherFunction(). Is there a way to pass in the scope of otherFunction()?
initialize: function() {
this.render();
if (joinedGoalList.get(this.model.id) != null) {
this.renderLeaveGoal();
} else {
this.renderJoinGoal();
}
},
events: {
"keypress #goal-update": "createOnEnter",
"click #join-goal": "joinGoal",
"click #leave-goal": "leaveGoal",
},
joinGoal: function() {
matches = joinedGoalList.where({id: this.model.get("id")});
if (matches.length == 0) {
joinedGoalList.create({goal_id: this.model.get("id")}, {wait: true, success: function() {
var self = this;
self.renderLeaveGoal();
}, error: function() {
console.log("error");
}});
}
},
renderLeaveGoal: function() {
console.log("render leave goal");
var template = _.template($("#leave-goal-template").html());
console.log(template);
$("#toggle-goal-join").html(template());
},
These are all under the same view.
Edit:
Hmm, now the problem is that I get this error:
Uncaught TypeError: Object [object DOMWindow] has no method 'renderLeaveGoal'. Does this seem that I saved the wrong scope?
Standard technique is to do something like
var self = this;
Then you can do
self.otherFunction();
In place of
this.otherFunction();
deltanovember's answer is correct, but he didn't explain why it's correct, and I feel that's important:
Scope is messy in Javascript. I've found the easiest way to keep track of it is to think about when something will execute. If a block of code executes right away, it's probably running in the same scope as "this". If a function is going to be called later, it's probably running in a completely different scope, with its own concept of "this".
In your example, the anonymous function you're providing as that success callback is going to run later, and it won't be scoped to the same "this" as the code that's running when said callback is defined. This is why you're getting an error: renderLeaveGoal was defined in the scope where the callback was defined, but not the scope where the callback will be executed.
Now, to make this more confusing, variables defined when a callback is defined will be available within that callback's scope. This is why deltanovember's answer works. By disguising "this" in a variable when the success callback is defined, the callback can still access it when it runs later, despite the completely different scope.
I hope that makes sense. Comment if it doesn't, and I'll try again :)
You can also use Underscore's bindAll function like so:
initialize: function() {
_.bindAll(this);
}
Behind the scenes, Underscore will replace all function calls in the object with proxied versions that set "this" to be the same "this" as your original object. Of course, if one of your methods has its own anonymous callback functions inside it, then you'll need to do the whole "self = this" dance; bindAll only fixes the context of your "outer" methods on the object.
I've gotten into the habit of using _.bindAll as the first line in each of my View's initialize() methods.
The function is attached to the view, so you need to call it off of the View object.
success: this.renderLeaveGoal
In this case, you don't need the anonymous function because the view functions are automatically bound to the view's context.
Related
In my knockout.js project I wrote some self invoking functions like this:
var addMarkers = function () {
ko.utils.arrayForEach(self.sectionList(), function (sectionItem) {
ko.utils.arrayForEach(sectionItem.placeList(), function (placeItem) {
placeItem.marker.addListener('click', function () {
map.panTo(placeItem.marker.getPosition());
});
});
});
}();
The function works without problems, however in JSLint the "var addMarkers" was highlighted as unused variable. That makes me wonder if I should the function like this, or just make anonymous because it is a better practice?:
function addMarkers (){ code to be executed };
Assigning the result of a self-executing function is often useful. In particular, I like to define my main viewmodel this way, because
I don't need a prototype for my viewmodel
I'm not likely to need more than one instance of a viewmodel
The reason you would use a self-executing function is that you need a local scope for whatever you're doing, to isolate it from surrounding scope. The reason you would assign a variable from it is that you want it to return a value you will use later. In your example, neither of these is true.
I am trying to create a basic javascript framework that you can pass different things into, including functions for it to execute later. Right now, I'm in a more simple testing phase, but I can't quite get the function calling to work. A piece of my code is here:
[My JS Fiddle][1]http://jsfiddle.net/mp243wm6/
My code has an object that holds different data, and I want to call the method later, but with data that is available at the time of creation. Here is a code snippet of the function that uses the function that is passed to the object:
clickMe : function() {
this.obj.click(function() {
this.func();
});
}
Any suggestions or things I should read are welcome.
The problem is that there're two different contexts:
clickMe : function() {
// here is one
this.obj.click(function() {
// here is another
this.func();
});
}
You can simple pass the function as parameter, like the following:
clickMe : function() {
this.obj.click($.proxy(this.func, this));
}
http://jsfiddle.net/mp243wm6/2/
The problem:
Considering your code in the JSFiddle, you have:
onClick : function() {
this.obj.click(function() {
this.func();
});
},
As noted, you have different contexts going on here.
Consider the snippet this.obj.click(function() { this.func(); }). The first this here is a reference to the framework.events object. The second this here is a reference to whatever will be this when this function get called. In the case of your JSFiddle, when this.func gets called, this is actually the DOM object that represents the <div id="test">TEST</div> node. Since it doesn't have a func function, calling func() on it causes:
Uncaught TypeError: undefined is not a function
You have to understand the following: you have to pass the correct this in which you want the function func to be called.
The solution:
A couple of ways to make it work as you would like:
1. with bind
this.obj.click(this.func.bind(this));
This way, you are telling: "call my this.func function, but make sure that it will be called using the this that I am passing as a parameter". Vanilla JS, no $.proxy stuff.
JSFiddle
2. with a copy of the reference to the actual function
onClick : function() {
var theFunctionReference = this.func;
this.obj.click(function() {
theFunctionReference();
});
},
This way, you will not rely on the value of this outside of the context of the framework.events object.
JSFiddle
The issue is that this is not bound to the correct object. I would suggest you look into Function.bind() because that creates a function with this pointing to the right thing.
I pulled the code below from this tutorial: tutorial
If you can bind this from a parent function to an anonymous inner function, then what do you do if you want to reference "this" in the anonymous inner function at some later time? Does "this" not exist for the anonymous inner function?
render: function ()
{
this.getAsyncData(function () {
this.specialFunction();
this.anotherSpecialFunction();
}.bind(this));
}
what do you do if you want to reference "this" in the anonymous inner function at some later time?
There is no way to refer to the value of this that the callback would have if we hadn't called .bind.
However, you don't have to use .bind, there are other ways to use the this value of the "parent" function: How to access the correct `this` context inside a callback?
Does "this" not exist for the anonymous inner function?
this is an implicit value in every function. Which value it has is determined by how the function is called, unless the function is explicitly bound to a certain value (via .bind).
For example, if you call a function "normally", such as func(), then this will refer to the global object (window in browsers). The API documentation usually explains which value this has inside a callback. If it doesn't mention anything explicitly, then you can assume that this refers to the global object, which is not very useful in most cases, and thus it is no problem to "override" it via .bind.
But even if this is set to a specific value in the callback, that value is often also passed as argument to the callback. Being able to access the value via this is just for convenience. But again, it depends on how the API is designed and is usually explained in the API documentation.
For more information about this, have a look at the MDN documentation.
The this context is set every time a function is called. If there is no context (as is the case with most callbacks), then this is lost.
It is more common to see:
var self = this;
// code using self instead of this here
The above is more browser-compatible due to .bind being relatively new.
When you have an anonymous callback, this usually refers to the window object. A good example is with setTimeout:
var someObject = {
set: function() { /* ... */ },
doSomething: function() {
// right here, "this" is "someObject"
setTimeout(function() {
// whereas right here, "this" is "window"
});
}
}
A lot of people solve this conflict like this:
var someObject = {
set: function() { /* ... */ },
doSomething: function() {
var that = this;
setTimeout(function() {
console.log(that) // => now it refers to "doSomething"
});
}
}
That's where bind comes into play.
See this jsfiddle:
var someObject = {
set: function() { /* ... */ },
doSomething: function() {
setTimeout(function() {
console.log(this) // => now it refers to "doSomething"
}.bind(this));
}
}
Edit
Felix Kling made a good comment on this answer. For this answer, I'm assuming you're calling someObject.doSomething(), and not calling the doSomething() method a different context.
If you can bind this from a parent function to an anonymous inner function, then what do you do if you want to reference "this" in the anonymous inner function at some later time?
You won't be able to, if you want to reference the outer this then set this to a variable and don't use bind.
var that = this;
Does "this" not exist for the anonymous inner function?
this always exists.
Simple example:
this.name = 'Bob';
function sayHello() {
return ['Hello,', this.name].join(' ');
}
console.log(sayHello()); // "Hello, Bob"
console.log(sayHello.bind({name: 'Ana'})()); // "Hello, Ana"
http://jsbin.com/xibis/1/edit
There's a little something about scope I just keep getting confused about:
this.init = function(){
var t = this;
setTimeout(function(){
// why is t still available here?
t.initgame();
// but not this?
this.initgame();
}, 500);
}
this.initgame = function() {
// yada yada
}
I get that inside an anonymous function, scope is different that outside of it.
But why, in the above example, is the variable "t" available inside the timeout function, while "this" is not working?
The problem is that setTimeout is called with window as scope.
Using a dedicated variable to store this (t) is a perfectly valid and usual solution.
On modern browsers, bind is sometimes convenient :
setTimeout((function(){
// use this
}).bind(this), 500);
When the anonymous function runs, it is no longer running as a member function of init, but rather a top-level function of window. As a result, this.initgame() has no meaning.
For example, running console.log(this) inside the timeout function returns as follows:
Window {top: Window, window: Window, location: Location, external:...
When you use var t = this, you assign reference to the current object, which works.
I am developing an add-on for Firefox (3.6.*). in the following code notify called from inside init works fine, but I get an error saying this.notify is not a function when it is called from within onPageLoad. Why is that?
Also when I change the call to myextobj.notify('title', 'msg'), it works. The same is true for accessing variables. So, what is the difference between this and the object name as a prefix?
var myextobj = {
init: function() {
this.notify('init', 'We are inside init');
...
var appcontent = document.getElementById("appcontent"); // browser
if(appcontent)
appcontent.addEventListener("DOMContentLoaded", this.onPageLoad, true);
},
onPageLoad: function(aEvent) {
this.notify('onPageLoad', 'We are inside onPageLoad');
...
},
notify: function (title, text) {
Components.classes['#mozilla.org/alerts-service;1'].
getService(Components.interfaces.nsIAlertsService).
showAlertNotification(null, title, text, false, '', null);
}
};
window.addEventListener("load", function() { myextobj.init(); }, false);
When you do this:
appcontent.addEventListener("DOMContentLoaded", this.onPageLoad, true);
you just add the function that is hold in onPageLoad as event handler. The connection to the object is lost and this will refer to the global object when executed.
Just create an anonymous function as you do for the load event:
var that = this; // capture reference to object
appcontent.addEventListener("DOMContentLoaded", function(event) {
that.onPageLoad(event);
// myextobj.onPageLoad(event); should also work in this case
}, true);
Remember that functions are first class objects in JavaScript, they can be passed around like any other value. Functions have no reference to an object they are defined on, because they don't belong to that object. They are just another kind of data.
To which object this refers to in a function is decided upon execution and depends on the context the function is executed in. If you call obj.func() then the context is obj, but if you assign the function to another variable before like var a = obj.func (that is wat you do with adding the event handler (in a way)) and then call a(), this will refer to the global object (which is window most of the time).
When onPageLoad is called for the event, 'this' would not be referring to your myextobj. Because it wasn't called in the context of your object myextobj.
The way I deal with this is, by having all member functions of an object using the following convention.
var myObj = {
.....
counter: 0,
.....
myFunction: function () {
var t = myObj;
t.myOtherFunc();
},
....
myOtherFunc: function() {
var t = myObj;
t.counter++;
}
};
See how, I'm aliasing myObj as t, to save on typing and making my intent of using this clear.
Now you can call your methods safely from any context without worrying about what this would be referring to. Unless you really want the standard behavior; in that case, you may like to look at the call and apply methods. This link might help: Function.apply and Function.call in JavaScript
You may also want to look at a recent addition to JavaScript (would be available in FireFox 4): the bind method: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/bind
Another link, which directly addresses your problem: https://developer.mozilla.org/en/DOM/element.addEventListener#The_value_of_this_within_the_handler
The other way to add an event listener without losing track of this is to pass this itself as the event listener. However you are limited in that the function is always called handleEvent, so it's less useful if you have many listeners (unless they are all for different events, in which case you can switch on the event's type).