I'm having to fix up a megamenu system for a site I've been handed, and I've run into a bug.
Once the DOM is ready (jQuery is included) the megamenu's render function is run. Within this there a listener is added for resize to rerun this function, re-rendering the code. However, I'm getting javascript errors that think that this render function when called from within the object is actually a function of the window so I get "Uncaught TypeError: Object [object DOMWindow] has no method 'render'".
But the function runs anyway!
here's a brief snapshot of the code:
var jkmegamenu=
{
...
render:function($)
{
...
$(window).bind("resize", function()
{
this.render($);
}
}
}
Now I've tried jkmegamenu.render($) (don't know what the dollar is for, works with or without due to jQuery I imagine), and I've tried defining self as self: this but the same error occurs. Its a fairly large file and I'm pretty busy so I'm only considering recodin开发者_如何学JAVAg it a proper jQuery object (e.g. http://www.phpied.com/3-ways-to-define-a-javascript-class/) if thats the only other option, otherwise does anyone know a quick fix?
The site is kent.ac.uk and you'll be able to see the errors popping up, especially on resize.
this
in JavaScript is a bit different than you may be used to from some other languages like C++, C#, or Java. It's defined entirely by how a function is called, not where the function is defined. More here: You must remember this
In your case, if you want to ensure that this
refers to your jkmegamenu
instance, you could use jQuery's proxy
function, which will create a closure for you behind-the-scenes that calls your function with the correct "context" (this
value):
var jkmegamenu=
{
...
render:function($)
{
...
$(window).bind("resize", $.proxy(function()
{
this.render($);
}, this));
}
}
Or just use your own closure directly:
var jkmegamenu=
{
...
render:function($)
{
...
var inst = this; // Remember `this`
$(window).bind("resize", function()
{
// Use it
inst.render($);
});
}
}
...which has the advantage that you can then use this
to refer to the DOM object you hooked the event handler to (okay, so less useful for window
, but quite useful when binding to other objects).
Closures are an extremely useful way of handling events. More here: Closures are not complicated
For your situation, since jkmegamenu
is a singleton, I'd probably just use the module pattern and not worry about this
at all:
var jkmegamenu = (function() {
var inst = {};
// ...
inst.render = jkmegamenu_render;
function jkmegamenu_render($)
{
$(window).bind("resize", function()
{
inst.render($);
// Or just jkmegamenu_render($);
});
}
// Could have some private functions here, just by
// virtue of not adding them to `inst`
// ...
return inst;
})();
That has the advantage of also giving your functions names, which helps your tools help you, and lets you have truly private functions as well.
You can also apply this pattern to factory functions ("classes" some would call them, although JavaScript doesn't have classes), with a bit of tweaking.
Javascript acts a little differently than you'd expect, in the sense that 'this' refers to whatever functional scope it is currently in. So 'this' does not point to what you'd expect. Instead, use:
var jkmegamenu=
{
...
render:function($)
{
...
var that = this;
$(window).bind("resize", function()
{
that.render($);
}
}
}
Within your resize function, the this
keyword refers to the DOM object being resized.
If you want to use the this
object from the parent function, your best bet is to create a temporary variable pointing to this
in the parent function. That variable will then be available in the scope of your resize function.
For more on how Javascript's this
works, see this article: http://javascriptweblog.wordpress.com/2010/08/30/understanding-javascripts-this/
精彩评论