美文网首页让前端飞Web前端之路
前端面试宝典之:手写bind方法

前端面试宝典之:手写bind方法

作者: 前端要摸鱼 | 来源:发表于2019-07-22 23:44 被阅读51次

    手动实现一个 bind 方法

    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
    

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

    STEP 3 补全特殊情况

    这一块我就不细说了,可以参照MDN上的Polyfill的代码。
    链接,点我跳转
    简单来说,有以下几个特殊情况要注意:
    1,并不是所有方法都能用bind,比如delete方法和参数的get/set方法,就是没有apply属性的,也就是not callable;碰到这种方法要抛出异常。
    2,需要保持方法原型链的完整。
    3,需要考虑实例化方法。

    好了,手动实现bind方法的部分到这里就结束了。
    所有代码已经上传到github上。
    源码链接
    如果本文对您有帮助,请给我一个小小的star~爱你

    相关文章

      网友评论

        本文标题:前端面试宝典之:手写bind方法

        本文链接:https://www.haomeiwen.com/subject/ohqnlctx.html