我们什么时候可以将未包装的函数作为参数传递

When can we pass an unwrapped function as a parameter

在Javascript和许多其他编程语言中,我们可以将函数作为参数传递给其他函数。这是函数式编程中的常见做法。

我们知道包装器需要在此处放置断点、查看它在堆栈跟踪中的位置、更好地控制参数或添加逻辑 before/after 调用。

是否有其他 objective 理由不将展开的函数作为参数传递?

myFunction1(x => myFunction2(x)) // wrapped
myFunction1(myFunction2) // unwrapped

您无需使用匿名函数包装函数即可将其指定为事件处理程序。如果函数应该获取事件对象,这很好。

window.addEventListener('click', myFunction)

此外,用匿名函数包装函数会阻止您删除事件处理程序,因为删除需要与添加的事件处理程序相同的事件处理程序。

这会起作用:

window. removeEventListener('click', myFunction)

这不会:

window.removeEventListener('click', e => myFunction(e))

如果您需要将更多参数传递给事件处理程序,您需要对其进行柯里化,并将返回的函数分配给一个变量,这样您就可以删除处理程序:

const createEventHandler = param => e => {}

const eventHandler = createEventHandler(true)

window.addEventListener('click', eventHandler)
// ...
window.removeEventListener('click', eventHandler)
call(e => foo(e))

完全相同:

call(foo)

只有一层不必要的间接访问 ;)

这与函数式编程或柯里化无关,我看不出它会如何影响您的调试体验。

当您想锁定某些参数或签名不兼容时,您通常会换行。例如,

call(a => foo(5, a))

但在这种情况下,您可能需要考虑柯里化。

询问“应该”使用一种或另一种技术主要是 off-limits 在 SO 中。

不过,我将回答一个相关问题,即何时可以和不可以使用该技术以及可能存在哪些(不利)优势。

你总是可以写foo (x => bar (x))。你不能总是写 foo (bar)。在 another Q & A 中可以找到原因的示例。根据我的经验,一个常见的例子是带有第二个参数的递归函数,该参数在初始调用中默认并传递给后续调用。这样的函数无法通过简单引用 map 成功传递,因为 map 提供了除预期值之外的其他参数。

但这是不寻常的情况。如果您的函数没有默认参数,则包装器似乎没什么用处。一个论点很简单,如果您要用 foo (x => bar (x)) 替换 foo (bar),为什么不更进一步并使用 foo (y => (x => bar (x)) (y))foo (z => (y => (x => bar (x)) (y)) (z)).

包装器可以工作,但什么也没有添加...除了,正如您指出的那样,作为挂起断点的地方。

这对您来说可能是合法的案例。这些天我在调试器上花的时间不多,但是当我这样做的时候,我可能偶尔会临时添加这样一个包装器。但我之后删除它们。我发现干净的代码非常重要,不必要的包装器只会让事情变得混乱。在代码审查中,我 运行 经常反对这种模式:

const foo = (...args) => {
    // do something with args
    return new Promise ((resolve, reject) => {
        bar (something).then(
            (a) => {
                resolve (a);
             }, (err) => {
                reject (err);
             }
        );
    });
}

我一直需要指出的可以更清楚地写成:

const foo = (...args) => {
    // do something with args
    return new Promise ((resolve, reject) => {
        bar (something) .then (resolve, reject);
    });
}

那我还要进一步指出,即使这样也可以更好地写成

const foo = (...args) => {
    // do something with args
    return bar (something);
}

关键是 resolvereject 周围的函数包装器只是混乱。 bar 周围的 Promise 包装器也是如此。它不会影响结果,对性能的影响很小,但它几乎无法平衡混乱。

(看,我试图在这里回避意见,但真的不能。)