陌生人,你好!我是​​春哥​​????,一个梦想是摸鱼????的前端工程师,希望我的文章可以帮到你~

bind 方法简介

在正式手写之前,你得知道 bind 方法是一个什么东西,在 MDN 上,如此介绍它:

bind()方法创建一个新的函数,在 bind()被调用时,这个新函数的 this 被 bind 的第一个参数指定,其余的参数将作为新函数的参数供调用时使用。

​链接:点我跳转​

如果你还没有看明白,不要紧,下面这个例子会向您展示它的用法:

const boy = {
age: 3,
getAge: function() {
return this.age;
}
}
const getAge = boy.getAge
getAge()
// undefined

const boyGetAge = getAge.bind(boy)
boyGetAge()
// 3
复制代码

上面的例子中,getAge 方法,虽然是 boy 的 getAge 方法,但是因为它在内部调用了 this,因此在运行时,this 指向了全局对象,也就是 window,因此 window.age 是 undfined。

但是 boyGetAge 方法,却可以让内部的 this 正确地指向 boy 对象,得到正确的 3。

这个例子,官方是这样描述的:


bind() 最简单的用法是创建一个函数,不论怎么调用,这个函数都有同样的 this 值。JavaScript 新手经常犯的一个错误是将一个方法从对象中拿出来,然后再调用,期望方法中的 this 是原来的对象(比如在回调中传入这个方法)。如果不做特殊处理的话,一般会丢失原来的对象。基于这个函数,用原始的对象创建一个绑定函数,巧妙地解决了这个问题:


接下来,我们看一下 bind 方法的第二个用法:​​偏函数​​。

function add(a, b) {
return a + b
}

add(1,2)
// 3
// 1 + 2 = 3

const newAdd = add.bind(null, 3)
newAdd(1,2)
// 4
// 3 + 1 = 4
复制代码

发现没?3 会取代 1 成为第一个入参,而 1 顺位到了第二个入参,2 因为成为了第三个入参而没有被调用。

官方如此解释:


bind()的另一个最简单的用法是使一个函数拥有预设的初始参数。只要将这些参数(如果有的话)作为 bind()的参数写在 this 后面。当绑定函数被调用时,这些参数会被插入到目标函数的参数列表的开始位置,传递给绑定函数的参数会跟在它们后面。


好,常用的用法主要就以上两点,知道了以上用法,我们就可以开始手写一个 bind 方法了。

STEP 1 实现上下文绑定

首先,bind 是属于方法的方法,而且它返回一个方法,并不改变原有方法

因此,它需要被挂到原型链上。

Function.prototype.myBind = function() {
const fnBound = function() {}
return fnBound
}
复制代码

如上,应该是我们这个方法的基本结构。

下面,让我们写一些代码,实现上下文绑定。

// 实现上下文绑定
Function.prototype.myBind = function(context) {
const fnToBind = this
const fnBound = function() {
return fnToBind.apply(context)
}
return fnBound
}
复制代码

接下来,我们可以做一个验证了:

const boy = {
x: 8,
getX() {return this.x}
}
const getX = boy.getX
getX.myBind(boy)()
// 8
复制代码

没错,最核心的功能已经完成????????,接下来让我们实现偏函数的部分。

STEP 2 实现偏函数

其实偏函数的原理很简单,bind中传入的参数数组,出现在执行函数的数组前端即可。

Function.prototype.myBind = function(context, ...args) {
const fnToBind = this
const fnBound = function(...fnArgs) {
return fnToBind.apply(context, args.concat(fnArgs))
}
return fnBound
}
复制代码

很容易吧?我们做个验证。

function add(a, b) {
return a + b
}
add.myBind(null, 10)(1, 2)
// 11
function add(a, b) {  return a + b}add.myBind(null, 10)(1, 2)// 11复制代码

看到结果,我们就知道,大功告成,基本已经达到了预期的目标。

STEP 3 补全特殊情况

这一块我就不细说了,可以参照MDN上的Polyfill的代码。

​链接,点我跳转​

简单来说,有以下几个特殊情况要注意:

  1. ​并不是所有方法都能用bind​​,比如delete方法和参数的get/set方法,就是没有apply属性的,也就是not callable;碰到这种方法要抛出异常。
  2. 需要保持方法​​原型链的完整​​。
  3. 需要考虑​​实例化方法​​。

好了,手动实现bind方法的部分到这里就结束了。所有代码已经上传到github上。

​源码链接​

如果本文对您有帮助,请您​​点赞​​+​​收藏​​+​​关注​​哟~

我是​​春哥​​,一个梦想是摸鱼的前端工程师~