js中bind()的模拟实现(二)

119 阅读3分钟

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

前言

今天继续来模拟一下bind()的实现,之前的一篇文章已经讲解了bind()模拟实现的一部分功能,其中包括了改变this的指向问题,参数的传递问题,可以点击这里跳转查看,本文来模拟bind()的另一个特点,那就是当bind()的返回函数当做构造函数的情况。

bind()的特性

  • 改变this的指向
  • 可以传递参数
  • 当bind()的返回函数作为构造函数时,这时this指向会失效,但传递进去的参数依然生效

我们在下面的代码的基础上来来实现第三点功能

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指向就这样写
            }

        }

在这里我们要知道的一点是当bind()当做构造函数被new的时候,这时候this的指向是实例化对象,关于this的指向问题可以点击这里查看。当知道了这点之后我们的主要工作就是改变this指向了。

只要将myBind中的return中改变this的指向即可,这时候就要考虑两种情况了,一种是当作为普通函数的情况,这是this应该指向context;另一种就是作为构造函数的情况,这时候就要指向this了(这里的this是实例化对象),所以上面的代码就要做一些更改了,如下

Function.prototype.myBind = function (context) {

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

            var fBound = function () {
                // 这里的arguments是指bind返回的那个函数传入的参数
                var returnFA = Array.prototype.slice.call(arguments);//这句话是把伪数组数组化
                return self.call(this instanceof fBound ? this : context, ...args.concat(returnFA)); // 这里就要做区分了,不能单纯的写一个context
                // return self.apply(this instanceof fBound ? this : context,, args.concat(returnFA)); 如果想用apply改变this指向就这样写
            }
            //把返回函数的原型指向调用函数(animal)的原型,这样实例就可以使用调用函数中的属性了
            fBound.prototype = this.prototype;
            return fBound

        }

测试一下:

var dog = {
            color: 'white'
        };

        function animal(name, age) {
            console.log(this.color);
            console.log(name);
            console.log(age);
        }
        animal.prototype.eat = function () {
            console.log('eat')
        }
        var bindFoo = animal.myBind(dog, '小白');
        bindFoo()
        var obj = new bindFoo('3');
        obj.eat()

结果如下图:

image.png

从结果可以看到,当是普通调用的时候this指向是dog。当当作构造函数的时候this指向的是obj,所以this.color打印了undefined。

fBound.prototype = this.prototype;这个操作存在一定的隐患,因为当你修改 fBound.prototype的时候this.prototype也会改变,因此可以改为如下结构

Function.prototype.myBind = function (context) {

            var self = this; 
            var args = Array.prototype.slice.call(arguments, 1);

            var fBound = function () {
                var returnFA = Array.prototype.slice.call(arguments);
                return self.call(this instanceof fBound ? this : context, ...args.concat(returnFA)); 
                // return self.apply(this instanceof fBound ? this : context,, args.concat(returnFA)); 
            }
           let fn =function (){}
            
            fn.prototype = this.prototype
            fBound.prototype = new fn()
            //这里也可以这样写,原因是object.create()的实现就是这样的,如下代码
            //fBound.prototype = Object.create(this.prototype)
            return fBound
            
        }

Object.create()的实现

    object.create = function(obj){
        functon fn(){}
        fn.prototype = obj
        return new fn()
    }

总结

虽然call()、apply()、bind()都能改变this的指向,但bind()做的事情感觉更加的复杂,实现起来也更不容易,有空再来复习,gogogo