Reputation: 8219
In the Polyfill section of the Array.prototype.forEach
js mdn, you can find the following check:
if (typeof callback !== 'function') {
throw new TypeError(callback + ' is not a function');
}
In this specific case, why bother checking for function? During my tests, calling the function directly yields the exact same behavior
var callback;
callback();
// Uncaught TypeError: callback is not a function
var callback = "";
callback();
// Uncaught TypeError: callback is not a function
var callback = 2;
callback();
// Uncaught TypeError: callback is not a function
var callback = [];
callback();
// Uncaught TypeError: callback is not a function
var callback = {};
callback();
// Uncaught TypeError: callback is not a function
Actually it's a little bit worse with this type of string concatenation, because we might see "[object Object] is not a function"
:
function newCallback(val){
if (typeof val !== 'function') {
throw new TypeError(val+ ' is not a function');
}
}
newCallback({});
//Uncaught TypeError: [object Object] is not a function
Upvotes: 4
Views: 229
Reputation: 40872
The behaviour of forEach is is defined that way 15.4.4.18 Array.prototype.forEach:
- Let
O
be the result of callingToObject
passing thethis
value as the argument.- Let
lenValue
be the result of calling the[[Get]]
internal method ofO
with the argument"length"
.- Let
len
beToUint32(lenValue)
.- If
IsCallable(callbackfn)
isfalse
, throw aTypeError
exception.- If
thisArg
was supplied, letT
bethisArg
; else letT
beundefined
.- Let
k
be0
.- Repeat, while
k
<len
[...]
And because the polyfill is implemented according to the specification, the test if callbackfn
is a function (4.) has to be done before the iteration starts (7.). And because of that the error will also be thrown even if callbackfn
is not called (in the case when len
is 0
).
Upvotes: 2
Reputation: 8219
I just noticed that they don't explicitly call the callback
like I did (callback()
) but rather they do callback.call()
, which no longer yields the same behavior and in this case makes sense to ensure the object is of type function.
// Call the Call internal method of callback with T as
// the this value and argument list containing kValue, k, and O.
callback.call(T, kValue, k, O);
Upvotes: 1
Reputation: 225095
In this specific case, why bother checking for function?
If the array is empty, there won’t be an attempt to call the function, but it should still fail.
[].forEach('foo')
There’s also a difference for non-empty arrays when element access has side-effects:
let foo = {
get [0]() {
alert(1);
}
};
Array.prototype.forEach.call(foo, …);
// alert shows up if the early type check is missing
Upvotes: 3