Reputation: 124
I wrote some code like this:
var a = new Array(10); // should be [undefined * 10]
var b = _.every(a, function(m){
if(_.isUndefined(m)){
return false;
}
return true;
});
I expect b is 'false', but it return 'true'. Why it return 'true'?
Then, I change to this:
var c = [undefined, undefined];
var d = _.every(c, function(m){
if(_.isUndefined(m)){
return false;
}
return true;
});
it return 'false' in d.
Why they are different?
You can test this in http://jsfiddle.net/3qj4B/3/
Upvotes: 1
Views: 474
Reputation: 413720
There's a subtle difference between initializing an array with the Array constructor and an initial size, and initializing with an explicit list of undefined
entries. The former (the Array constructor) doesn't create properties corresponding to the indexes, while the explicit initialization does.
I suspect that Underscore is using the native .forEach()
when it can, and that won't call its callback for uninitialized indexes. Thus, in the first test, the callback to _.every()
is never being called at all.
edit — the Array constructor does something more-or-less the same as:
var a = []; a.length = 10;
When you extend an array by increasing its length like that, the new implicit index positions aren't initialized. It's the basic difference between the non-existence of a property, and the presence of a property with no value. In both cases, a dereference of the property results in undefined
. Thus:
var o = {};
if (o.something == undefined) // this will be true
and then:
var o = { something: undefined };
if (o.something == undefined) // also true
One way to tell the difference between the two situations is the in
operator:
if ('something' in o) // only true in the second case
Upvotes: 1
Reputation: 12042
When you create an array using the class initializer you are creating an array with 10 space of memory available to be used, but none of them is yet initialized. So you're not looping over anything.
Now look at the source of every
:
_.every = _.all = function(obj, iterator, context) {
iterator || (iterator = _.identity);
var result = true;
if (obj == null) return result;
if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context);
each(obj, function(value, index, list) {
if (!(result = result && iterator.call(context, value, index, list))) return breaker;
});
return !!result;
};
result
is set to be true
and it is returned since each
does no iterations. That's why you're returning true
.
If, in your fiddle, you try to add a console.log(m)
inside the handler you'll see no logs in the console since each
iterates 0 times.
Upvotes: 2