Javascript: Illegal invocation - javascript

I am experiencing an issue. I am working with a fairly old application which has the following logic:
var Page;
var OriginalSubmit;
function init() {
Page = document.forms[0];
OriginalSubmit = Page.submit;
Page.submit = newPageSubmit;
}
function newPageSubmit() {
validate();
OriginalSubmit();
}
When the OriginalSubmit is executed, I get an Illegal invocation argument. I've read enough to understand that is because the this reference has changed (at least I think that's the case), but what I am struggling with is the correct way to fix it. The JavaScript for this is a mess, wrapped up with JSPs and global functions everywhere, so making this correct would be quite an effort, so I am looing to see if there's a chance for a less invasive solution, thanks.

When you assign OriginalSubmit = Page.submit you're obtaining a reference to the .submit() function, but when you call it as just plain OriginalSubmit() you're losing the Page context variable that will be passed as this to that function:
myObject.method(); // calls "method" with "this === myObject"
var method = myObject.method();
method(); // calls "method" with "this === window"
Instead, use this:
OriginalSubmit.call(Page); // sets "this" to "Page"
Note that even then, it's possible that re-assigning a method of a native DOM element won't work portably in all browsers, and may not work at all.

Related

Javascript: Create a sub-class within a class [duplicate]

I thought I had a reasonable understanding of the this object in JavaScript. When dealing with objects, callbacks, and both events and handlers, I haven't had a problem with it since time immemorial. Now, however, all has changed.
I've fallen head over heels in love with JavaScript. Pure JS, that is, not jQuery, prototype.js, dojo... So naturally, I've taken to using closures. In some cases, though, this is catching me off guard here. Take this snippet for one:
function anyFunc(par)
{
//console.log(par);
console.log(this);
}
function makeClosure(func)
{
return function(par)
{
return func(par);
}
}
var close = makeClosure(anyFunc);
close('Foo');
var objWithClosure = {cls:makeClosure(anyFunc),prop:'foobar'};
objWithClosure.cls(objWithClosure.prop);
var scndObj = {prop:'Foobar2'};
scndObj.cls = makeClosure;
scndObj.cls = scndObj.cls(anyFunc);
scndObj.cls(scndObj.prop);
In all three cases, this logs as the window object. It's an easy fix, of course:
function makeClosure(func)
{
return function(par)
{
return func.call(this,par);
}
}
This fix works, I put it here to avoid people answering this, without explaining what I need to know: why is this behaving the way it does here?
ensures the caller is effectively the object that the closure belongs to. What I fail to understand is this:
Sure enough, this points to the window object in the first case, but in other cases, it shouldn't. I tried logging this in the makeClosure function just before returning, and it did log the object itself, not the window object. But when the actual closure is used, this is back to pointing to the window object. Why?
The only thing I can think of is that, by passing the anyFunc function as an argument, I'm actually passing window.anyFunc. So I tried this quick fix:
function makeClosure(func)
{
var theFunc = func;
return function(par)
{
theFunc(par);
}
}
With the expected results, this now points to the objects, but again: Why? I have a few idea's (theFunc is a reference to the function in the local scope [this > private: theFunc]?), but I'm sure there are people here with a lot more know-how when it comes to JS, so I was hoping to get some more explanation or links to articles worth reading from them...
Thanks
Update
Here's a fiddle, may be I left something out, but here this logs all sorts of things ;)
Edit/Update 2
The case that confuses me is here.
Final Edit
Ok, This is getting a rather messy post. So to clarify: What I was expecting was a behaviour similar to this:
function makeClosure()
{
function fromThisFunc()
{
console.log(this);
}
return fromThisFunc;
}
var windowContext = makeClosure();
windowContext();
var objectContext = {cls:makeClosure()};
objectContext.cls();
What caught me, was that the function anyFunc wasn't declared within the correct scope, and therefore, this pointed to the window object. I found this out by reading an ancient scroll I found somewhere on the web.
But something a little more complicated has happened because the function object now referred to by globalVar was created with a [[scope]] property referring to a scope chain containing the Activation/Variable object belonging to the execution context in which it was created (and the global object). Now the Activation/Variable object cannot be garbage collected either as the execution of the function object referred to by globalVar will need to add the whole scope chain from its [[scope]] property to the scope of the execution context created for each call to it.
So what I needed to do, was simplify rather then complicate things:
function fromThisFunc()
{
console.log(this);
}
function makeClosure(funcRef)
{
//some code here
return funcRef;
}
That should work, right?
PS: I'll except Alnitak's answer, but special thanks goes to Felix Kling for all the patience and info.
As soon as you call:
return func(par);
You're creating a new scope (with its own this) and in this case because you haven't specified an object, this === window usually or undefined in strict mode. The called function does not inherit whatever this was in the calling scope.
Ways to set a value for this are:
myobj.func(par); // this === myobj
or
func.call(myobj, ...) // this === myobj
There are also:
apply
bind
arrow functions, where this is set to the same value as the outer execution context (also see MDN:Arrow functions )
The value of this depends only on whether you call the function as a method or as a function.
If you call it as a method, this will be the object that the method belongs to:
obj.myFunction();
If you call it as a function, this will be the window object:
myFunction();
Note that even if you are in a method that belongs to an object, you still have to call other methods in the object using the method syntax, otherwise they will be called as functions:
this.myOtherFunction();
If you put a method reference in a variable, you will detach it from the object, and it will be called as a function:
var f = obj.myFunction;
f();
The call and apply methods are used to call a function as a method even if it's not a method in the object (or if it's a method in a different object):
myFunction.call(obj);

why is it necessary to wrap function call in a function body

I often see something like the following in JavaScript:
$("#sendButton").click(function() {
sendForm();
}
Why is it necessary to wrap the call to sendForm() inside a function? I would think that doing it like this would be more readable and less typing.
$("#sendButton").click(sendForm);
What are the advantages/disadvantages to each approach? thanks!
There's typically two cases where you'd want to use the former over the latter:
If you need to do any post-processing to the arguments before calling your function.
If you're calling a method on an object, the scope (this reference) will be different if you use the second form
For example:
MyClass = function(){
this.baz = 1;
};
MyClass.prototype.handle = function(){
console.log(this.baz);
};
var o = new MyClass();
$('#foo').click(o.handle);
$('#foo').click(function(){
o.handle();
});
Console output:
undefined
1
Probably one too many answers by now, but the difference between the two is the value of this, namely the scope, entering sendForm. (Also different will be the arguments.) Let me explain.
According to the JavaScript specification, calling a function like this: sendForm(); invokes the function with no context object. This is a JavaScript given.
However, when you pass a function as an argument, like this: $(...).click(sendForm), you simply pass a reference to the function for later invocation. You are not invoking that function just yet, but simply passing it around just like an object reference. You only invoke functions if the () follows them (with the exception of call and apply, discussed later). In any case, if and when someone eventually calls this function, that someone can choose what scope to call the function with.
In our case, that someone is jQuery. When you pass your function into $(...).click(), jQuery will later invoke the function and set the scope (this) to the HTML element target of the click event. You can try it: $(...).click(function() { alert(this); });, will get you a string representing a HTML element.
So if you give jQuery a reference to an anonymous function that says sendForm(), jQuery will set the scope when calling that function, and that function will then call sendForm without scope. In essence, it will clear the this. Try it: $(...).click(function() { (function() { alert(this); })(); });. Here, we have an anonymous function calling an anonymous function. We need the parentheses around the inner anonymous function so that the () applies to the function.
If instead you give jQuery a reference to the named function sendForm, jQuery will invoke this function directly and give it the scope that it promises to always give.
So the answer to your question becomes more obvious now: if you need this to point to the element target of the click when you start work in sendForm, use .click(sendForm). Otherwise, both work just as well. You probably don't need this, so skip the anonymous function.
For those curious, scope can be forced by using the JavaScript standard apply or call (see this for differences between the two). Scope is also assigned when using the dot operator, like in: obj.func, which asks of JavaScript to call a function with this pointing to obj. (So in theory you could force obj to be the scope when calling a function by doing something like: obj.foo = (reference to function); obj.foo(); delete obj.foo; but this is a pretty ugly way of using apply.
Function apply, used by jQuery to call your click handler with scope, can also force arguments on the function call, and in fact jQuery does pass arguments to its click handlers. Therefore, there is another difference between the two cases: arguments, not only scope, get lost when you call sendForm from an anonymous function and pass no parameters.
Here you are defining an anonymous event handler that could call multiple functions inline. It's dirty and tough to debug, but people do it because they are lazy and they can.
It would also work like your second example (how I define event handlers):
$("#sendButton").click(sendForm)
Something you get by defining your event handlers inline is the ability to pass event data to multiple functions and you get this scoped to the event object:
$("#sendButton").click(function(event) {
sendForm();
doSomethingElse(event);
andAnotherThing(event);
// say #sendButton is an image or has some data attributes
var myButtonSrc = $(this).attr("src");
var myData = $(this).data("someData");
});
If all you are doing is calling sendForm, then there isn't much difference, in the end, between the two examples you included.
$("#sendButton").click(function(event) {
if(event.someProperty) { /* ... */ }
else { sendForm({data: event.target, important: 'yes'}); }
}
However, in the above case, we could handle arguments passed to the callback from click(), but if the sendForm function is already equipped to handle this, then there's no reason why you wouldn't place sendForm as the callback argument if that is truly all you are doing.
function sendForm(event) {
// Do something meaningful here.
}
$("#sendButton").click(sendForm);
Note that it is up to you where you handle the differing layers of logic in your program; you may have encapsulated certain generic functionality in a sendForm function then have a sendFormCallback which you pass to these sorts of function which handle the interim business of event/callback processing before calling sendForm itself.
If you are working in a callback-heavy environment, it would be wise to separate significant functionality from the callback triggers themselves to avoid callback hell and promote maintainability and readability in your source code.
It's just to lock scope. When you wrap that sendForm() in the anonymous function that closes over the current scope. In other words, the this will be kept with it. If you just pass sendForm then any calls to this will come from the calling scope.
This is a good question for learning about scope in javascript, and questioning conventions.
Nope, that second example is perfectly valid.
99.9% of jQuery examples use the first notation, that doesn't mean you need to forget basic JavaScript syntax.

JavaScript closure and the this object

I thought I had a reasonable understanding of the this object in JavaScript. When dealing with objects, callbacks, and both events and handlers, I haven't had a problem with it since time immemorial. Now, however, all has changed.
I've fallen head over heels in love with JavaScript. Pure JS, that is, not jQuery, prototype.js, dojo... So naturally, I've taken to using closures. In some cases, though, this is catching me off guard here. Take this snippet for one:
function anyFunc(par)
{
//console.log(par);
console.log(this);
}
function makeClosure(func)
{
return function(par)
{
return func(par);
}
}
var close = makeClosure(anyFunc);
close('Foo');
var objWithClosure = {cls:makeClosure(anyFunc),prop:'foobar'};
objWithClosure.cls(objWithClosure.prop);
var scndObj = {prop:'Foobar2'};
scndObj.cls = makeClosure;
scndObj.cls = scndObj.cls(anyFunc);
scndObj.cls(scndObj.prop);
In all three cases, this logs as the window object. It's an easy fix, of course:
function makeClosure(func)
{
return function(par)
{
return func.call(this,par);
}
}
This fix works, I put it here to avoid people answering this, without explaining what I need to know: why is this behaving the way it does here?
ensures the caller is effectively the object that the closure belongs to. What I fail to understand is this:
Sure enough, this points to the window object in the first case, but in other cases, it shouldn't. I tried logging this in the makeClosure function just before returning, and it did log the object itself, not the window object. But when the actual closure is used, this is back to pointing to the window object. Why?
The only thing I can think of is that, by passing the anyFunc function as an argument, I'm actually passing window.anyFunc. So I tried this quick fix:
function makeClosure(func)
{
var theFunc = func;
return function(par)
{
theFunc(par);
}
}
With the expected results, this now points to the objects, but again: Why? I have a few idea's (theFunc is a reference to the function in the local scope [this > private: theFunc]?), but I'm sure there are people here with a lot more know-how when it comes to JS, so I was hoping to get some more explanation or links to articles worth reading from them...
Thanks
Update
Here's a fiddle, may be I left something out, but here this logs all sorts of things ;)
Edit/Update 2
The case that confuses me is here.
Final Edit
Ok, This is getting a rather messy post. So to clarify: What I was expecting was a behaviour similar to this:
function makeClosure()
{
function fromThisFunc()
{
console.log(this);
}
return fromThisFunc;
}
var windowContext = makeClosure();
windowContext();
var objectContext = {cls:makeClosure()};
objectContext.cls();
What caught me, was that the function anyFunc wasn't declared within the correct scope, and therefore, this pointed to the window object. I found this out by reading an ancient scroll I found somewhere on the web.
But something a little more complicated has happened because the function object now referred to by globalVar was created with a [[scope]] property referring to a scope chain containing the Activation/Variable object belonging to the execution context in which it was created (and the global object). Now the Activation/Variable object cannot be garbage collected either as the execution of the function object referred to by globalVar will need to add the whole scope chain from its [[scope]] property to the scope of the execution context created for each call to it.
So what I needed to do, was simplify rather then complicate things:
function fromThisFunc()
{
console.log(this);
}
function makeClosure(funcRef)
{
//some code here
return funcRef;
}
That should work, right?
PS: I'll except Alnitak's answer, but special thanks goes to Felix Kling for all the patience and info.
As soon as you call:
return func(par);
You're creating a new scope (with its own this) and in this case because you haven't specified an object, this === window usually or undefined in strict mode. The called function does not inherit whatever this was in the calling scope.
Ways to set a value for this are:
myobj.func(par); // this === myobj
or
func.call(myobj, ...) // this === myobj
There are also:
apply
bind
arrow functions, where this is set to the same value as the outer execution context (also see MDN:Arrow functions )
The value of this depends only on whether you call the function as a method or as a function.
If you call it as a method, this will be the object that the method belongs to:
obj.myFunction();
If you call it as a function, this will be the window object:
myFunction();
Note that even if you are in a method that belongs to an object, you still have to call other methods in the object using the method syntax, otherwise they will be called as functions:
this.myOtherFunction();
If you put a method reference in a variable, you will detach it from the object, and it will be called as a function:
var f = obj.myFunction;
f();
The call and apply methods are used to call a function as a method even if it's not a method in the object (or if it's a method in a different object):
myFunction.call(obj);

Why does the content of the function load, but not the function when it is called?

I'm having an issue with a function I'm calling (using jQuery).
My issue is that when I attempt call a function within another function, that first one is not being called. However, when I place the contents of the failed function into the one calling said failed function, the code executes fine. I'll show you what I'm talking about:
$('form.register .submit').click(validateRegister);
function submitStart() {
var $this = $(this);
$this.parent().addClass('processing');
var $processing = $('#processing');
$processing.show();
var $endNote = $this.parent().find('#endNote')
$endNote.slideDown();
}
function validateRegister(){
submitStart();
}
See, when the contents of submitStart() are places into the validateRegister(), they code executes. However, when calling the function as it shows in this code, it fails to work. I've considered scope (at least to my knowledge) to no avail.
Any help is extremely appreciated, thank you!
You're losing your expected this value, so the submitStart is likely getting called, but doesn't work properly.
Do this:
function validateRegister(){
submitStart.call( this );
}
Here you're calling the submitStart method via the .call() method, which is available on function instances.
The .call() method lets you manually set the value of this in the function you're calling.
So because you've assigned validateRegister as the handler, jQuery makes sure the value of this is your element. But since validateRegister is calling another function, it carries the responsibility of making sure that function has the proper this value.
Just keep in mind that when you call any function like this:
someFunction();
...the value of this in that function will be window, unless you manually set it to something else.
var $this = $(this); is returning the window.
this will no longer be referencing the clicked element but will represent window instead. You can change submitStart so that it accepts a parameter and pass this to it.
I think you may have an issue with this not being what you think it is.
Try this:
submitStart.call(this)
The problem here is this. When you use validateRegister as an event hander, jQuery automatically makes sure that this refers to the DOM element that dispatched the event. But when you call your separate function, the scope isn't being set correctly, so this probably refers to the window object (that's the default when you refer to this in the global scope). To fix it, the easiest way would be to pass the element along as an argument:
function submitStart(element) {
var $this = $(element);
$this.parent().addClass('processing');
var $processing = $('#processing');
$processing.show();
var $endNote = $this.parent().find('#endNote')
$endNote.slideDown();
}
function validateRegister(){
// "this" is the DOM element
submitStart(this);
}
As #Andrew noted, you can also set the value of this in submitStart by using the .apply() method of the submitStart function.

Best approach to avoid javascript's “this” mistakes in XBL

Talking about XBL is not exactly talking about javascript. So I'll create this question that's related to this one, but now about XBL, where I'm not the creator of the root javascript code, but just of the methods and event handlers inside the bindings.
--
In some cases, the this keyword may not refer to the object I expect it to. (recent example: in an key event, in my XBL)
What's the best approach to avoid this kind of mistake?
For now, I'm using always the getElementById (or $.fn from jQuery), but I'm not sure if it's the best approach.
--update
A little bit more details:
Within a XBL method the only way to access the element that was defined in the Xul file (the GUI description file) without using "this" (as it may not be the "this" I expect) is with getElementById, and this makes the code not reusable, so I'm looking for alternatives..
As you've heard elsewhere, the problem is that the this parameter isn't necessarily the object you first thought of, especially when you're talking about event handlers and the like. The best way I found is to write a small function that will bind some object as the this variable and return another function that uses the binding to call the original function.
Take a look at this code:
var bindFunction = function(self, f) {
return function() {
return f.apply(self);
};
};
var foo = function() {
alert(this.hello);
};
var bar = {
hello: "Hello there"
};
var boundFoo = bindFunction(bar, foo);
boundFoo();
What I have is a function called bindFunction that takes an object (called self) and some function, and returns another function that will call the passed-in function applying the self object as the this variable.
The rest of the code is just some test code: a foo function that will alert this.hello, a bar object with a hello member, and how you can bind foo with bar.
In essence, I've taken a function that accepts no parameters and generated another function that will ensure the first is always called with the correct this variable. Ideal for event handlers.
(Sorry, but I know nothing about XBL, so I hope this will be of use.)
If there's not a best answer, a good approach could be use javascript files instead of XBL for the implementation. Looking the Xulrunner source code, looks like most code does this.
But I still would like to use XBL, if there was a way to work fine with it..

Categories

Resources