深入理解一件事物最好的方式之一,就是用自己的方法模拟一遍。这篇文章会用实际的例子带你走一遍 bind 是如何运作的,并在此基础上,一步步带你实现它。
我们直接来一个例子:
例1
function
输出结果为:
这里面有两件事是需要你注意的:
一个是:bind 会改变函数的 this 指向,在这个例子中,fn 本来的 this 是指向 window 的,而 bind 将 this 的指向从 window 改到了 obj。
第二件事是:任何时候,都要牢记 bind 返回的是一个函数,而不是函数执行的结果。我们必须加上一个 () ,让这个函数真正执行起来。
需要注意的是,下面这种方法也可以实现相同的效果:
例2
function
我们可以这样做归纳总结,在使用 bind 的时候,你可以有两次机会传入参数,第一次是在 bind 函数里面,第二次是在运行 bind 返回的匿名函数的时候。
当然,我们可以再拓展一下,利用 bind,我们可以把一个本来有多个参数的函数分两批传入。来看下面这个例子:
例3
function
输出的结果为:
至此,本着 bind 的这两个性质,我们来自己实现自己的 bind,myBind。
第一步:显然所有的 function 都可以使用 bind,那么,我们的 myBind 应该加在 function 的原型上。于是,我们有:
function
第二步,bind 要改变 this 的指向,所以我们把 myBind 的第一个参数设置成 context,即,我们要改变 this 指向的对象。任何函数原本的函数都指向 window,因此,我们给它设定默认值 window
function
第三步,bind 要传入返回函数的参数,但这个参数的数量是不固定的,因此我们用拓展运算符 ... 来处理它:
function
四,首先,我们需要明确,在用 bind 的时候,比如 fn.bind(),我们返回的函数其实就是 fn 本身,只是改了 this 而已,所以,为了获取到 fn,我们需要在 myBind 里用 this 拿到这个 fn。需要注意的是,匿名函数中的 this 指向并不是 fn,而是 window。另一点是,myBind 自己可以传参,内部的匿名函数也可以,根据 例3,我们了解到 bind 可以分批传参的特点,因此,我们需要把外部参数和内部参数利用 concat 组合起来。
function
如果你不理解下面这句话
_this
我们可以这么理解
_this
我们测试一下:
function
输出结果为
符合预期。
这里补充说明一点,实现 myBind 的这套方法是有一个专有名词的,叫柯理化函数思想,或者说,预先处理的思想。(引自周啸天老师)
当我们的 myBind 被任何外界的变量引用的时候,比如:
document
myBind 的私有作用域就不能被销毁,形成闭包。这个时候,context 和 outerArg 都被 myBind 存储了起来。等到内部的匿名函数真正被执行的时候,我们把这些 myBind 已经存好的值直接拿来用就可以了。这就是闭包的作用之一,保存。