[路飞]黑魔法-uncurryThis

303 阅读2分钟

uncurryThis

故事背景是这样的。

难得在项目搞完送去打包之后,得到了一段空闲时间,于是想看一看 babel 是怎么写 Set 的 polyfill。最后并没有看完,因为很快新的需求又来了。

但是在这个过程中还是看到了一段很有趣的代码。就是 uncurryThis。记不住所以省略了 !bind 的情况。babel 返回的是三目运算符的结果。

    const FunctionProto = Function.prototype
    const call = FunctionProto.call
    const bind = FUnctionProto.bind
    
    export const uncurryThis = bind.bind(call)

然后就可以这样使用。

    const toUpperCase = uncurryThis(String.prototype.toUpperCase)
    toUpperCase('abc') // --> 'ABC'
    
    // or
    
    function bar() {
        console.log(this.foo)
    }
    uncurryThis(bar)({ foo: 'hello' }) // 打印 'hello'

相信不少人在一开始 bind.bind(call) 的时候已经晕了,但是先不说,这样一通操作之后,用起来就很奇妙。magically use --/

接下来一步步解剖 bind.bind(call) 里面究竟发生了什么。

首先我们看一下普通的 bind 是怎么使用的。

const fn = Object.prototype.toString.bind([]) // 将对象原型的 toString 方法给 [] 数组使用

fn() 的时候得到 '[object Array]'

bind 是生成一个函数,这个函数的 this 是 bind 时的第一个参数。内部实现等价于

    function bind(context) {
        // 调用 bind 的函数 就是 xxx.bind
        const fn = this
        return function () {
            // 绑定this,并且将新函数的参数作为参数。
            // 调用bind封装的函数依然有原函数的返回操作,跟随原函数的返回结果
            return fn.apply(context, arguments)
        }
    }

所以当我们 bind.bind(call) 的时候,就是返回了一个将 call 作为 bindthis 一个新的函数。当我们调用这个新的函数时返回的是 bind 生成的一个新函数,绑定了 arguments

    bind.bind(call) 得到:
        function () {
            return bind.apply(call, arguments)
        }
    然后调用这个函数 得到:arguments[0] 是传入要封装的函数
        bind.apply(call, arguments) 的执行结果 // 已经把参数绑上了,这也是一个bind
        即 call.bind(...arguments) 再包装了一层bind
        
        使用的时候其实就等价于 上面的arguments[0].call(这次调用时的传参)

用上面的例子就是等价于:

    String.prototype.toUpperCase.call('abc')
    bar.call({ foo: 'hello' })

其实就是用call显式的进行改绑和隐式的进行改绑。但是 bind.bind(call) 毕竟还是影响性能的。