原生实现bind方法

想要用原生js写一下bind方法,嗯,让我们先来了解一下bind方法吧,先看一波官方的解释(MDN):

bind() 函数会创建一个新函数(称为绑定函数),新函数与被调函数(绑定函数的目标函数)具有相同的函数体。当新函数被调用时this值绑定到bind()的第一个参数,该参数不能被重写。绑定函数被调用时,bind()也接受预设的参数提供给原函数。
一个绑定函数也能使用new操作符创建对象:这种行为就像吧原函数当成构造器。提供的this值被忽略,同时调用时的参数被提供给模拟函数。

哎呀我的妈,官方的解释真的是好绕。我们来举个栗子说:

var foo = {
    value:'233',
    getValue: function() {
        console.log(this.value);
        console.log(arguments[0], arguments[1])
    }
}

var newGetValue = foo.getValue.bind(foo, 1);
newGetValue(2);
//233
//1 2
简单来说:
1、函数foo.getValue调用bind的时候,会返回一个新的函数newGetValue。
2、newGetValue和foo.getValue函数体是一毛一样的。
3、newGetValue函数被调用时的this是指向对象foo(传给bind的第一个参数)。
4、bind函数被调用时传递的参数,会在newGetValue被调用时传递,并且排在实参的最前面。
5、new newGetValue() 时,会把newGetValue当成一个构造函数,this自动被忽略,参数依旧可以传。

好了,接下来我们来聊聊原生js怎么实现?在这之前希望你了解过call、apply是如何用原生实现的。
Function.prototype.mybind = function(context) {
    var self = this;
    var args = [];//保存bind函数调用时传递的参数
    for(var i = 1, len = arguments.length; i< len;i ++) {
        args.push(arguments[i]);
    }
    
    //bind()方法返回值是一个函数
    return function() {
        //哇,新创建的函数传进来的参数可以在这里拿到哎!!
        var bindArgs = Array.prototype.slice.call(arguments);
        self.apply(context, args.concat(bindArgs))
    }
}
OK,我们已经实现了bind的方法指定this值,和传递参数的功能了。

接下来看一下构造函数的效果如何实现:忽略this的值,传参依旧正常。我一直在想,怎么知道调用的时候是通过构造调用的呢?
这个简单,被当做构造函数调用的时候this指向一个新创建的对象;而被当普通函数调用的时候,this是指向window的。那么问题又来了?我们如何判断this指向这个新对象呢?别忘了,构造函数new一个新对象必然关系到原型链继承这些事情,是的,bind函数不仅会复制被调函数的函数体,原型链也要复制。这样我们就看 this instanceof self ? 新对象是否继承自被调函数呀,如果是,那么就是被当做构造函数调用的。
来看代码:
Function.prototype.mybind = function (context) {
    if (typeof this !== "function") {
        throw new Error(this + "is not a function");
    }
    var self = this;
    var args = [];
    for (var i = 1, len = arguments.length; i < len; i++) {
        args.push(arguments[i]);
    }

    var fbound = function () {
        var bindArgs = Array.prototype.slice.call(arguments);
        self.apply(this instanceof self ? this : context, args.concat(bindArgs));
    }
    fbound.prototype = Object.create(self.prototype);
    //返回的函数不仅要和 被调函数的函数体相同,也要继承人家的原型链
    return fbound;
}

最后,别忘了,如果调用bind方法的不是一个函数,要报错哦~
这篇博客也是我自己看了bind的原生实现之后对知识的一个梳理和总结,让我能更好的理解和掌握。如有错误,恳请指正。

参考文章:JavaScript深入之bind的模拟实现


你可能感兴趣的:(前端基础)