Reputation: 41
I'm studying underscore.js and javascript programming. While I'm seeing the source of underscore, I can't understand some part of it.
I can't understand why it uses apply with 'this' in the _.negate, because the other parts receive 'context's and use them. Below is the source code.
https://github.com/jashkenas/underscore/blob/master/underscore.js
_.negate = function(predicate) {
return function() {
return !predicate.apply(this, arguments);
};
};
The places where _.negate is used are in _.reject and _.omit. Seeing code of _.reject, it receives 'context' and use it for the context.
_.filter = _.select = function(obj, predicate, context) {
var results = [];
if (obj == null) return results;
if (nativeFilter && obj.filter === nativeFilter) {
return obj.filter(predicate, context);
}
each(obj, function(value, index, list) {
if (predicate.call(context, value, index, list)) results.push(value);
});
return results;
};
_.reject = function(obj, predicate, context) {
return _.filter(obj, _.negate(predicate), context);
};
Please give me some explanation. Thanks in advance.
Upvotes: 2
Views: 978
Reputation: 434685
The apply
call is doing two things at once:
this
that will be in effect inside predicate
.predicate
without having to care how many arguments are being used.(2) should be clear enough if you know about arguments
in JavaScript; arguments
is an Array-like object which holds the function's current argument list, all functions in JavaScript are variadic (despite what their definition says) and arguments
is how you work with the arguments when you don't know how many there are.
(1) should be clear with a quick look at how the _.negate
return value is used. Inside _.filter
, the predicate
is invoked using call
:
predicate.call(context, value, index, list)
That sets this
to context
inside predicate
. If predicate
is actually _.negate(original_predicate)
then that's effectively this:
var f = function() {
return !original_predicate.apply(this, arguments);
};
f.call(context, ...)
so original_predicate
will get called like original_predicate.apply(context, arguments)
and the specified this
(i.e. context
) will get be in effect when original_predicate
executes.
If _.negate
just did this:
return function(a, b, c) {
return !predicate(a, b, c);
};
then two bad things would happen:
context
would be lost._.filter
but maybe not for other uses of _.negate
.Losing track of context
would break a lot of code, using Function.prototype.apply
to specify the this
just happens to solve the argument list problem for free.
Upvotes: 4