我认为如果您向后工作,可以更清楚地解释这一点。
上下文:
假设我们想要小写一个字符串数组。可以这样做:
[‘A’, ‘B’].map(s => s.toLowerCase())
假设,无论出于何种原因,我都想让这个调用更通用。我不喜欢 s 绑定到 this 而粗箭头绑定到 toLowerCase()。
这个怎么样?
[‘A’, ‘B’].map(String.prototype.toLowerCase)
这不起作用,因为map 将元素作为第一个参数传递,但String.prototype.toLowerCase 不接受任何参数。它期望输入字符串作为this 传递。
所以一个问题是我们可以创建一个wrapper 函数来完成这项工作吗?
[‘A’, ‘B’].map(wrapper(String.prototype.toLowerCase))
wrapper 返回一个函数,该函数将传递的第一个参数转换为 this 以供 String.prototype.toLowerCase 使用。
我声称你的uncurryThis === wrapper。
证明:
所以我们不要试图一下子理解unCurryThis。相反,让我们使用一些公式将unCurryThis 转换为更易于理解的内容。
先说一些公式:
instance.function(...args)
=== (instance.constructor.prototype).function.call(instance, ...args)
=== (Class.prototype).function.call(instance, ...args) [1]
=== (Class.prototype).function.bind(instance)(...args) [2]
例如,
Class === String
instance === 'STRING'
function === toLowerCase
args === []
---
'string'.toLowerCase()
=== ('STRING'.constructor.prototype).toLowerCase.call('STRING')
=== (String.prototype).toLowerCase.call('STRING')
=== (String.prototype).toLowerCase.bind('STRING')()
所以让我们盲目地应用这些公式,而不必担心令人困惑的 uncurryThis 是什么样子:
'string'
=== (wrapper)(String.prototype.toLowerCase)('STRING')
=== (uncurryThis)(String.prototype.toLowerCase)('STRING')
=== (Function.bind.bind(Function.call))(String.prototype.toLowerCase)('STRING')
// Function.bind is not really the generic form because it's not using the prototype
// Here Function is an instance of a Function and not the constructor.prototype
// It is similar to calling Array.bind or someFunction.bind
// a more correct version would be
// someFunction.constructor.prototype.bind === Function.prototype.bind, so
=== (Function.prototype.bind.bind(Function.prototype.call))(String.prototype.toLowerCase)('STRING')
// Apply formula 2
// instance.function(...args) === (Class.prototype).function.bind(instance)(...args) [2]
// Class === Function
// function === bind
// instance === Function.prototype.call
// ...args === String.prototype.toLowerCase
=== instance.function(...args)('STRING')
=== (Function.prototype.call).bind(String.prototype.toLowerCase)('STRING')
// Apply formula 2 again
// Class == Function
// function == call
// instance === String.prototype.toLowerCase
// ...args === 'STRING'
=== instance.function(...args)
=== (String.prototype.toLowerCase).call('STRING')
// Apply formula 1
instance.function(...args) === (Class.prototype).function.call(instance, ...args) [1]
// Class === String
// function === toLowerCase
// instance === 'STRING'
// args === []
=== instance.function(...args)
=== 'STRING'.toLowerCase(...[])
=== 'STRING'.toLowerCase()
// So we have
(wrapper)(String.prototype.toLowerCase)('STRING')
=== (uncurryThis)(String.prototype.toLowerCase)('STRING')
=== 'STRING'.toLowerCase()
=== 'string'
反向证明:
所以你可能想知道“这家伙是怎么推导出uncurryThis 函数的”?
您可以反转证明来推导它。我只是从上面复制方程式但反过来:
'STRING'.toLowerCase()
=== (String.prototype.toLowerCase).call('STRING') // apply formula [1]
=== (Function.prototype.call).bind(String.prototype.toLowerCase)('STRING') // apply formula [2]
// At this point, you might wonder why `uncurryThis !== (Function.prototype.call).bind)
// since it also takes (String.prototype.toLowerCase)('STRING')
// This is because passing in (Function.prototype.call).bind) as an argument
// is the same as passing in Function.prototype.bind
// `this` binding isn't done unless you call
// (Function.prototype.call).bind)(String.prototype.toLowerCase)
// at that exact moment.
// If you want to be able to pass unCurryThis as a function, you need to bind the
// Function.prototype.call to the Function.prototype.bind.
=== (Function.prototype.bind.bind(Function.prototype.call))(String.prototype.toLowerCase)('STRING') // apply formula 2
=== (Function.bind.bind(Function.call))(String.prototype.toLowerCase)('STRING') // un-generic-ize
=== (uncurryThis)(String.prototype.toLowerCase)('STRING')
=== (wrapper)(String.prototype.toLowerCase)('STRING')
=>
unCurryThis === wrapper === Function.bind.bind(Function.call)
仍然很混乱,但尝试写出 Class、function、instance 和 args 是我每次应用公式 [1] 和 [2] 时的内容,这应该是有意义的.