Hi guys I have a function which accepts this as a parameter - 'this' referring to the dom element which upon clicked should run a function. The thing is that I want this function to be called after a small delay however passing the variable term this doesn't work as when the function is executed 'this' then doesn't refer to the object in passed in the parameter but to the window object.
How can I get this done?
You could capture this:
var t = this;
window.setTimeout(function() {
// use the t variable here
}, 2000);
PrototypeJS adds the bind() method to Function.prototype. This method allows you to bind a function and arguments to the context of a particular object. Simply,
window.setTimeout((function() {
alert(this);
}).bind(this), 2000);
The best part is that this method is now part of the ECMA-262 specification, which JavaScript is based upon, and native implementations are rolling out into modern browsers. PrototypeJS will only add this method if it's not already implemented.
I've set up an example script at http://jsfiddle.net/rLpbx/.
Related
I am working on a project that uses some JS prototyping and jQuery.
My issue, I believe, is how 'this' is being used.
In my code, I have some jQuery in a function that I will be prototyping. The jQuery looks like this:(in this code, 'target' is a jQuery object passed when IMAGE_UPLOADER is first created.)
document.getElementById(target.find('.file_selector').prop('id')).addEventListener("change", this.FileSelectHandler, false);
In this event listener, there is a function called FileSelectHandler. This function is being called just fine. However, within this function, there is a call to a second function. Here is a short version of the function:
FILE_UPLOADER.prototype.FileSelectHandler = function(e) {
this.FileDragHover(e);
}
This is where the error comes up. JS is complaining that the function 'FileDragHover' does not exist. It, of course DOES exist and is defined as follows:
FILE_UPLOADER.prototype.FileDragHover = function(e) {}
I hope this is enough info to understand the problem. If not, please let me know and I can add more.
You can use $.proxy() to pass the custom execution handler to the event handler, this inside the event handler refers to the dom element where the listener is attached - it is the same as Function.bind() but jQuery version supports IE < 9.
document.getElementById(target.find('.file_selector').prop('id')).addEventListener("change", $.proxy(this.FileSelectHandler, this), false);
The handler registration can be simplified using jQuery to
target.find('.file_selector').change($.proxy(this.FileSelectHandler, this))
If an object obj has a function func and you do obj.func() the function is called in context of obj where this in that function then referes to obj.
If you however do something like this:
var callback = obj.func;
callback();
Then callback is not called in the context of obj anymore. For browsers the context is then window. This is what is happening if you pass the function as callback to the addEventListener.
Depending how your callback is used it can be called with another context, for event listeners it is the DOM element, so this in your code refers to the DOM element.
To solve this problem you can used Function.prototype.bind (you need to check the browser support if it is usable for you or use a polyfill for it, that can be found on the mdn page), you can use jQuery.proxy or create a closure and maintain the context yourself.
The this variable refers to the invoking object not the object that the method is declared on. So if I have someObject.someFunction(); then someObject is the invoking object and the value of this in the function.
To demonstrate:
var obj1 = {
name:"obj1",
say:function(){
console.log(this.name);
}
};
var obj2 = {name:"obj2"};
obj2.say = obj1.say;
obj2.say()//logs obj2
In the above code say was declared on obj1 but invoked from obj2.
More on the this value, constructor functions and prototype here.
I've seen it done differently in code out there, but is there any benefit or reason to doing a (blank params) .call / .apply over a regular () function execution.
This of course is an over-simplified example
var func = function () { /* do whatever */ };
func.call();
func.apply();
VERSUS just the simple parenthesis.
func();
Haven't seen any information on this anywhere, I know why call/apply are used when params are passed.
When you call a method with func();, this variable inside the method points to window object.
Where as when you use call(...)/apply(...) the first parameter passed to the method call becomes this inside the method. If you are not passing any arguments/pass null or undefined then this will become global object in non strict mode.
Yes, there is an important difference in some cases. For example, dealing with callbacks. Let's assume you have a class with constructor that accepts callback parameter and stores it under this.callback. Later, when this callback is invoked via this.callback(), suddenly the foreign code gets the reference to your object via this. It is not always desirable, and it is better/safer to use this.callback.call() in such case.
That way, the foreign code will get undefined as this and won't try to modify your object by accident. And properly written callback won't be affected by this usage of call() anyways, since they would supply the callback defined as an arrow function or as bound function (via .bind()). In both such cases, the callback will have its own, proper this, unaffected by call(), since arrow and bound functions just ignore the value, set by apply()/call().
What does the 'function' do in the following?
$('.event-row').on('mouseover',function(){
arc.event_handler.event_row_over();
});
$('.event-row').on('mouseover',arc.event_handler.event_row_over );
There's a very important difference.
The first one will call the function with the context its this value as the event_handler object.
The second one will call the function with the context its this value as the DOM element to which the handler is bound.
So the first one preserves the expected calling context this value, which may be required by the function.
In the first case with the anonymous function this inside that function is bound to the DOM element that caused the event. This is a convention that is common in browsers and also done when binding events natively. When calling arc.event_handler.event_row_over(); however, this is re-bound to arc.event_handler inside event_row_over; as it's called as an object method and in such a case this points to the object on which the method was called. The method will be called without any arguments.
In the second case you register the function arc.event_handler.event_row_over for the event. When called jQuery sets this to the related element so inside event_row_over, this points to that element. arc.event_handler is not available in there unless there is some other variable that points to it. jQuery also passes the event object as the first argument so the method is called with that argument.
Usually object methods expect this to be their object, so in almost every case you want to use the anonymous function to wrap the call. In case the element matters, pass this as an argument to the method.
Another way, without an anonymous function, would be using the bind() method every function has:
$('.event-row').on('mouseover', arc.event_handler.event_row_over.bind(arc.event_handler));
However, only modern browsers support this natively.
In the first case you are enclosing the function call in an anonymous function.
In the second case you are just assigning the function pointer..
First off, it seems like there is an extra dot in there.. arc.event_handler.event_row_over.(); should probably be just arc.event_handler.event_row_over();
And all the anonymous function does is it calls a member function named event_row_over of the arc.event_handler object; and it doesn't return anything.
The 'function' keyword will creates a new closure and encapsulate the scope. Good article on closures https://developer.mozilla.org/en-US/docs/JavaScript/Guide/Closures.
The first case, you have an additional function wrapper. This is useful when you want to do something else before calling the real event handler 'arc.event_handler.event_row_over()' for example you may do something like below:
$('.event-row').on('mouseover',function(){
doPreEventHandling();
arc.event_handler.event_row_over();
doPostEventHandling();
});
On the other hand you may even extract that annonymous function to be a named function and call as below:
var eventHandler = function(){
doPreEventHandling();
arc.event_handler.event_row_over();
doPostEventHandling();
};
$('.event-row').on('mouseover', eventHandler);
All above will be just similar in behavior, but more wrapper functions you have more abstraction you gain. But it will compromise performance and sometimes readability.
The context/scope of the function will not be the same.
Also, with the second one,
$('.event-row').on('mouseover',arc.event_handler.event_row_over );
you're getting the event object as an argument.
I have a function:
myObject.myFunction = function(callback){
callback();
}
and a callback
randomObject.callBack = function(){
console.log(this);
}
if I call randomObject.callBack() directly, I get the parent object in the console. However, if I call myObject.myFunction(randomObject.callBack), it logs a DOM Element.
How can I access the parent object?
Note
I do not know the name of the callbacks parent object ahead of runtime.
The context (i.e. this value) of an object is determined at the time the function is run, not at the time it is defined. Passing randomObject.callBack to another function does not send the context (the randomObject bit); it just sends the function.
Presumably the context is set when myFunction calls it. Since you aren't explicitly providing a context (e.g. with call or apply), the context will be the window object.
You can change this by explicitly saying what the context of the function should be before it is run. You can do this with the bind method:
myObject.myFunction(randomObject.callBack.bind(randomObject))
Now when you call callback inside myFunction, randomObject will be logged.
Note that bind is relatively new; not all browsers support it. The MDN page I linked to above has a bit of code that will make it work in all browsers.
This happens because when you invoke a function without an object, inside the function this will point to Window object.To avoid this we usually do like this
myObject.myFunction = function(callback){
callback();
}
randomObject.callBack = function(){
console.log(this);
}
function proxyCallback(){
randomObject.callBack();
}
myObject.myFunction(proxyCallback);
In javascript, this refers to the object context in which a function is called. This is not necessarily related to any object on which it has been defined.
You can think of it as though functions are not defined as members of objects, but called as members of objects.
There are four things that this might resolve to, depending on context.
A newly created object, if the function call was preceded by the new keyword.
The Object to the left of the dot when the function was called.
The Global Object (typically window), if neither of the above are provided.
The first argument provided to a call or apply function.
In your situation, something like this might be appropriate:
myObject.myFunction(function(){randomObject.callBack()});
This creates a closure so that within myFunction, callback is called as a member of randomObject.
This question already has answers here:
Preserving a reference to "this" in JavaScript prototype functions [duplicate]
(7 answers)
Closed 6 years ago.
What is the correct way to preserve a this javascript reference in an event handler stored inside the object's prototype? I'd like to stay away from creating temp vars like '_this' or 'that' and I can't use a framework like jQuery. I saw a lot of people talk about using a 'bind' function but was unsure of how to implement it in my given scenario.
var Example = function(foo,bar){
this.foo = foo;
this.bar = bar;
};
Example.prototype.SetEvent = function(){
this.bar.onclick = this.ClickEvent;
};
Example.prototype.ClickEvent = function(){
console.log(this.foo); // logs undefined because 'this' is really 'this.bar'
};
I find bind() being the cleanest solution so far:
this.bar.onclick = this.ClickEvent.bind(this);
BTW the other this is called that by convention very often.
Check out the MDN document on bind: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/bind
Using this functionality, you can change the scope (what this is):
Example.prototype.SetEvent = function(){
this.bar.onclick = this.ClickEvent.bind(this);
};
Be aware, however, that this is a new addition to EMCA and thus may not be supported in all user agents. There is a pollyfill available at the MDN document linked above.
The problem with bind is that is only supported by IE9+.
The function can be polyfilled with es5-shim, but it's not completely identical to the native implementation:
Caveat: the bound function has a prototype property.
Caveat: bound functions do not try too hard to keep you from manipulating their arguments and caller properties.
Caveat: bound functions don't have checks in call and apply to avoid executing as a constructor.
Another alternative can be jQuery.proxy:
$(elem).on('click', $.proxy(eventHandler, this));
This is even more helpful if you want to remove the event handler later, because when a function goes through the proxy method, jQuery generates a new guid value and then applies that guid to both the core function as well as the resultant proxy function, so that you can use the original function reference to unbind an event handler callback that has been proxied:
$(elem).off('click', eventHandler);
Other solution: use the "arrow functions" introduced by ES6. Those have the particularity to not change the context, IE what this points to. Here is an example:
function Foo(){
myeventemitter.addEventListener("mousedown", (()=>{
return (event)=>{this.myinstancefunction(event)}; /* Return the arrow
function (with the same this) that pass the event to the Foo prototype handler */
})());
}
Foo.prototype.myinstancefunction = function(event){
// Handle event in the context of your Object
}
Arrow function specs # MDN
Edit
Be carefull with it. If you use it client-side and you can't be sure of the capabilities of the JS interpreter, note that old browser won't recognize arrow functions (see CanIUse stats). Use this method only if you KNOW what will run it (recent browsers only & NodeJS apps)