Javascript function pointers with Jquery causing domManip is not a function error
JQuery Version
jQuery JavaScript Library v1.4.4
Problem
The solution may be blatantly obvious, however I'm scratching my head.
The problem is while doing some code optimisation I came across a loop that called append on a jquery element a few times and it was a rather large loop. So it looked something like this:
var l开发者_JS百科ist_of_goodies = [1,2,3,...];
$.each(list_of_goodies, function(val) {
$('div.toaddto').append(val);
...some more code...
$('div.toaddto').append(otherval);
});
As you can see not really optimised so I tried to use function pointers so it looked something like this:
var list_of_goodies = [1,2,3,...];
var toaddtoAppend = $('div.toaddto').append;
$.each(list_of_goodies, function(val) {
... the other code...
toaddtoAppend(val).append(otherval);
});
It may not seem like a huge optimisation, but it's a large list and this can save a lot of lookup time especially in the older IE's. This however causes an error.
The Error
this.domManip is not a function
Unfortunately this is from the minified jQuery, so there isn't much more info, it seems to happen within wrapInner() however.
Question
Is this a scope issue or a reference issue? I've tested it without using jQuery and the function pointer worked.
$.each or a for loop end up with the same results. Anyone know where I'm going wrong here? I know very little about how javascript handles function pointers, especially when they're supposed to be associated to an instance of something as opposed to just static, so forgive any ignorance here.
Browsers Tested In
- Firefox 3.6
- IE 6, 7 & 8
- Chrome 9.0.something
Regardless of the browser the results are always the same, which seems to suggest it's not how the browser is handling the pointer that's causing it to break.
Test it Yourself
<html>
<head>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js"></script>
<script type="text/javascript">
$(function(){
var iterables = [];
for (var i = 0; i < 1000; i++) iterables.push(i);
var divAppend = $('#test').append;
$.each(iterables, function(val) {
divAppend(val);
});
});
</script>
</head>
<body>
<div id="test"></div>
</body>
</html>
Thanks, dennmat
When you write var divAppend = $('#test').append
, you're storing a reference to jQuery's standard append
function.
It's equivalent to writing var divAppend = $.fn.append
—$('#test')
isn't stored anywhere.
When you call divAppend
as a normal function, its this
is window
, not a jQuery object.
You need to store a function that explicitly calls $('#test').append(...)
, or use divAppend.call($('#test'), val)
to explicitly call append
on the right object.
When you do this:
var toaddtoAppend = $('div.toaddto').append;
you do indeed get a reference to the "append" function, but you lose the relationship to the jQuery object. Thus when it's called later, there's no this
reference for it to work on.
There's a method on Function instances available in newer browsers (and JavaScript engines) called "bind" that could do what you want here:
var toaddtoAppend = $('div.toaddto').append.bind($('div.toaddto'));
(I wouldn't write it that way of course, with the redundant call, but it's just an illustration.) That would give you a reference to a function that would call "append" with this
being a reference to that jQuery object.
It is a scope issue, this will refer to window by the time you call the function. You could wrap the call to append in another function and store that for later use.
However, the real thing that you'll want to prevent is is not the lookup of a function but querying the DOM tree multiple times.
You can store the jQuery object with all the items in a variable and refer to that.
var toaddto = $('div.toaddto');
$.each(list_of_goodies, function(val) {
toaddto.append(val);
...some more code...
toaddto.append(otherval);
});
If you insist on storing the reference to the function you could do that as well:
var toaddto = $('div.toaddto');
var myappend = toaddto.append;
$.each(list_of_goodies, function(val) {
myappend.call(tooaddto, val);
...some more code...
myappend.call(tooaddto, otherval);
});
精彩评论