bind()的实现

118 阅读3分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第17天,点击查看活动详情

前言

call()、apply()、bind()是面试中常被问到的改变this指向的三剑客,对call()apply()感兴趣的可以点跳转到相应文章。bind()的使用方式方式和call()、apply()有这很大的区别,在实现上也更加的复杂,下面具体来看看。

bind()干了什么?

image.png 如上图,这是mdn中对bind()函数的描述,由此我们可值bind()的特点是

  • 调用bind()会返回一个新函数
  • 调用bind()得到的函数中this指向bind()的第一个参数,这点和call()及apply()是一样的

实现bind()

由于bind()也可改变this的指向,我们这里可以利用call()来实现改变this指向的效果,代码如下

Function.prototype.myBind = function (context) {
            var self = this;
            return function () {
                return self.call(context);
            }

        }
        dog = {
            color:'white'
        }
        function animal(){
            console.log(this.color)
        }
        var result = animal.myBind(dog)
        result()

上面代码的打印结果是white,这说明已经成功的改变了this的指向,要不然打印的应该是undefined。

接下来模拟一下bind()中的参数传递,首先来看一下bind()的参数都可以怎样传递,如下代码

 var dog = {
            color: 'white'
        };

        function animal(name, age) {
            console.log(this.color);
            console.log(name);
            console.log(age);

        }

        var result = animal.bind(dog, '小白');
        result('3');

这里的打印结果如下图:

image.png 由此我们可以发现使用bind()的时候参数可以在调用bind()的时候传入,也可以在执行bind()的返回函数的时候传入,下面看一下要如何来实现这点。

要实现上面的这点,我们要分为3步

  • 首先拿到调用bind()时候传入的参数
  • 再拿到执行bind()返回函数时传入的参数
  • 把这两步拿到参数都传递给目标函数执行即可
Function.prototype.myBind = function (context) {

            var self = this; //这里的this是调用myBind的那个函数
            // 获取myBind函数中从第二个参数到最后一个参数
            var args = Array.prototype.slice.call(arguments, 1);

            return function () {
                // 这里的arguments是指bind返回的那个函数传入的参数
                var returnFA = Array.prototype.slice.call(arguments);//这句话是把伪数组数组化
                return self.call(context, ...args.concat(returnFA));
                // return self.apply(context, args.concat(returnFA)); 如果想用apply改变this指向就这样写
            }

        }

上述代码就实现了两次参数合到一起传递给目标函数,下面实验一下

 var dog = {
            color: 'white'
        };

        function animal(name, age) {
            console.log(this.color);
            console.log(name);
            console.log(age);

        }

        var result = animal.myBind(dog, '小白');
        result('3');

结果如下

image.png 结果和bind()的结果一样,非常的nice。这里多提一嘴Array.prototype.slice.call(arguments),这里的Array.prototype.slice.call()可以把一个带有length属性的对象转换为数组,如下例子:

    var ls = {
            0:'小白',
            1:3,
            length:2
        }
        //[].slice.call()和Array.prototype.slice.call()一样
        var result = Array.prototype.slice.call(ls)
        console.log(result)

结果如下:

image.png

总结

上述就是模拟bind()功能的一部分,为什么说一部分呢,因为bind()还有个特点,那就是当bind()返回的函数当做构造函数的时候,这时绑定的this失效,并且传入的参数还有用,这点后续再来补充