功能的冗余类型检查?

Redundant type checking for function?

Array.prototype.forEach js mdn 的 Polyfill 部分,您可以找到以下检查:

if (typeof callback !== 'function') {
    throw new 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

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

实际上这种字符串连接有点糟糕,因为我们可能会看到 "[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

In this specific case, why bother checking for function?

如果数组为空,则不会尝试调用该函数,但它 should still fail.

[].forEach('foo')

当元素访问具有 side-effects 时,non-empty 数组也有区别:

let foo = {
    get [0]() {
        alert(1);
    }
};

Array.prototype.forEach.call(foo, …);
// alert shows up if the early type check is missing

forEach 的行为是这样定义的 15.4.4.18 Array.prototype.forEach:

  1. Let O be the result of calling ToObject passing the this value as the argument.
  2. Let lenValue be the result of calling the [[Get]] internal method of O with the argument "length".
  3. Let len be ToUint32(lenValue).
  4. If IsCallable(callbackfn) is false, throw a TypeError exception.
  5. If thisArg was supplied, let T be thisArg; else let T be undefined.
  6. Let k be 0.
  7. Repeat, while k < len [...]

并且因为polyfill是按照规范实现的,所以测试if callbackfn是一个函数(4.)必须在迭代开始之前完成( 7.).因此,即使未调用 callbackfn(在 len0 的情况下),也会抛出错误。

我只是注意到他们没有像我那样明确地调用 callback (callback()) 而是他们调用 callback.call(),这不再产生相同的行为并且在这种情况对于确保对象是函数类型是有意义的。

// 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);