I have a class:
function RustEditor() {
this.init = function() {
var saveButton = this.container.find("button.saveButton");
saveButton.click(function(){this.save();});
};
...
When I click the button, it complains that this.save is not a function. This is because "this" does not refer to the instance of RustEditor here, but to the button. What variable can I use inside that callback closure to point to the instance of RustEditor? I could use rust.editor (it's name in the global scope) but that's smelly code.
Common practice is to enclose the this value like so:
function RustEditor() {
this.init = function() {
var self = this;
var saveButton = this.container.find("button.saveButton");
saveButton.click(function(){self.save();});
};
Update with suggestion from tvanfosson:
this gets rebound when the event handler is invoked and thus you need to capture the reference to the class at the time the object is created with a variable that will retain that reference in the closure.
Within RustEditor() you could first copy a reference to the button and use that.
Related
I wonder if somebody could please help me understand something that seems odd with JS?
The below code works. The function inlineEditEvent.init() is called, and then t.copy() is called correctly (where var t = this;).
However, if I was to replace that with this.copy(), I get the error this.copy is not a function.
What's the difference here? Why does the below work, but not the way as described in the last paragraph? Thanks.
jQuery(function($){
$(document).ready(function(){inlineEditEvent.init();});
inlineEditEvent = {
init : function(){
var t = this;
/** Copy the row on click */
$('#the-list').on('click', '.row-actions a.single-copy', function(){
return t.copy();
});
}, // init
copy : function(){
// Do stuff here
}
} // inlineEditEvent
});
You're setting t as a context variable of this (of your init function). Once inside your click handler, this is now referring to the click handler, no longer the init function. Therefore, this.copy() is not a function.
this refers to this within the functions scope. That's why you need to set a self variable, so it's accessible within the scope of the function. Considering you're using jQuery, you could use $.proxy:
$.proxy(function(){
return this.copy();
},this)
t.copy(); appears in a different function to var t = this;. The value of this changes inside each function.
When you say var t= this; it refers to what this meant in that context. Later on when you are trying to refer to this, it is referring to a.single-copy instead since that is the new context it is in.
I have a simple script which calls a function that is set in the function that the call is made...
But i get an "undefined function" error.
My script is:
function messages_document(messages){
messages = JSON.parse(messages);
function del_msg(id){
result = call_file('del.php',id);
if(result){
messages[id].length = 0;
}
}
var output = [];
output.push('<p align="center"><b><u>My Messages</u></b></p> <br/><br/>');
for(var id in messages){
output.push('Delete Message');
}
document.getElementById('main').innerHTML = (output.join(''));
}
I'm curious if i have misunderstood how scope works because i get:
del_msg is not defined
Any ideas why this is ?
You have a problem with the scope in that "del_msg" is defined: You need that function to be in the global scope, but you're creating it inside a function "messages_document" (that has its own scope).
Function statements are defined in the current scope, not globally. In this case, that means that del_msg exists only within the scope of messages_document. However, you invoke it via an onclick attribute: these attributes are evaluated globally. Since del_msg doesn't exist globally, it doesn't work.
You have two options. One is to make del_msg global, by defining it outside any other functions. However, this pollutes the global namespace, generally a bad thing. Better would be to apply it to your a element within the scope. This is going to require building the elements via DOM methods, rather than by HTML
var para = document.createElement('p'),
id,
link;
para.innerHTML = '<b><u>My Messages</u></b></p> <br/><br/>';
para.align = 'center';
for(id in messages){
link = document.createElement('a');
link.href = '';
link.onclick = del_msg;
link.innerHTML = "Delete message";
para.appendChild(link );
}
document.getElementById('main').appendChild(para);
Try declaring it like
var del_msg = function(id)...
Sorry confused. you are using the function on the window scope do declare it out of the other function.
The function is called with the onclick global scope.
What you have to understand here is that you are creating html code, but the onclick will be called when user click and not when you vreate the html.
I am trying to write some javascript functions for a web application.
Following the advice from here: How do I declare a namespace in JavaScript?
I am trying to make the code unobtrusive by wrapping everything in named functions, however I am running into difficulty accessing properties in the parent function when inside a child anonymous function.
For example:
var title_picker = new function(){
// This property should be public
this.callback=function(){};
var ok=function(){
var title = $('input#title').val();
callback(title) // <---
}
...
When inside the "ok" function, what is the best way to reference the "callback" property?
With the code as written, no there isn't.
You can access variables from the scope of the parent, unless they are overwritten in the narrower scope.
this is always overwritten.
You can copy this to another variable that remains in scope though:
var title_picker = new function(){
var self = this; // Copy this to a variable that stays in scope
// This property should be public
this.callback = function(){};
var ok=function(){
var title = $('input#title').val();
self.callback(title) // self is still in scope
}
...
Perhaps something like this:
var title_picker = new function(){
// This property should be public
this.callback=function(){};
var that = this;
var ok=function(){
var title = $('input#title').val();
that.callback(title) // <---
}
...
Though there are many frameworks that do this kind of thing for you (yui, dojo, prototype...)
This question is best explained with some code, so here it is:
// a class
function a_class {
this.a_var = null;
this.a_function = a_class_a_function;
}
// a_class::a_function
function a_class_a_function() {
AFunctionThatTakesACallback(function() {
// How to access this.a_var?
});
}
// An instance
var instance = new a_class();
instance.a_function();
From within the callback in AFunctionThatTakesACallback(), how does one access this.a_var?
You'll need to expand the scope of this by creating a local variable that references it, like this:
function a_class_a_function() {
var self = this;
AFunctionThatTakesACallback(function() {
console.log(self.a_var);
});
}
The reason why you need to do this is because the this reference within the AFunctionThatTakesACallback function is not the same this as the current object, it will likely reference the global windowobject instead. (usually not what you want).
Oh, did I mention that this is called a closure?
You could try using the call method of function objects, which lets you specify a value for this:
myFunction.call(this, args...)
But I think that in this case it would probably be more straightforward to pass 'this' in as one of the parameters to the callback.
When you call instance.a_function(), you're really calling a_class_a_function with instance as this, so you can modify a_class_a_function like so:
function a_class_a_function() {
var self = this;
AFunctionThatTakesACallback(function() {
// do something with self.a_var
});
}
The problem here is that if you attempt to call a_class_a_function without calling it from an instance, then this will likely refer to the global object, window.
This is a very old problem, but I cannot seem to get my head around the other solutions presented here.
I have an object
function ObjA() {
var a = 1;
this.methodA = function() {
alert(a);
}
}
which is instantiated like
var myObjA = new ObjA();
Later on, I assign my methodA as a handler function in an external Javascript Framework, which invokes it using the apply(...) method.
When the external framework executes my methodA, this belongs to the framework function invoking my method.
Since I cannot change how my method is called, how do I regain access to the private variable a?
My research tells me, that closures might be what I'm looking for.
You already have a closure. When methodA is called the access to a will work fine.
Object properties are a different thing to scopes. You're using scopes to implement something that behaves a bit like ‘private members’ in other languages, but a is a local variable in the parent scope, and not a member of myObjA (private or otherwise). Having a function like methodA retain access to the variables in its parent scope is what a ‘closure’ means.
Which scopes you can access is fixed: you can always access variables in your parent scopes however you're called back, and you can't call a function with different scopes to those it had when it was defined.
Since a is not a property of this, it doesn't matter that this is not preserved when calling you back. If you do need to get the correct this then yes, you will need some more work, either using another closure over myObjA itself:
onclick= function() { myObjA.methodA(); };
or using Function#bind:
onclick= myObjA.methodA.bind(myObjA);
yes, you're right. Instead of a method reference
var myObjA = new ObjA();
libraryCallback = myObjA.methodA
pass a closure
libraryCallback = function() { myObjA.methodA() }
If you are using jQuery javascript framework, easiest way is to use proxy:
$('a').click($.proxy(myObjA, 'methodA'));
I'd do this:
function ObjA() {
this.a = 1;
this.methodA = function() {
alert(this.a);
}
}
function bindMethod(f, o) {
return function(){
return f.apply(o, arguments);
}
}
var myObjA = new ObjA();
myObjA.methodA = bindMethod(myObjA.methodA, myObjA);
...
Where bindMethod binds the methodA method to always be a method of myObjA while still passing on any arguments which function() {myObjA.methodA()} doesn't do.