美文网首页Web前端之路
bind方法的实现及原理

bind方法的实现及原理

作者: 成熟稳重的李先生 | 来源:发表于2019-08-08 17:55 被阅读1次

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

看以上描述,bind本身不调用原函数,只是装饰了下原函数来达到一些额外的效果,而call和apply却是实实在在的调用了原函数
接着上一节的this,我们再来一段代码

var view= {
    element: 'div',
    bindEvent: function() {
        this.element.onclick = function() { // 此处意思是,点击了元素之后,调用view对象的emitClick方法(但是,这当然是行不通的,因为this发生了变化)
            this.emitClick()
        }
    },
    emitClick: function() {
        this.element.addClass('active')
    }
}

分析以上代码,初始化调用“view.bindEvent”之后,接着会调用“this.element.onclick”,上节我们说过,js引擎在调用函数时,并不是如你所见的“fn()”,它会这样“fn.call(this)”,至于此时传入的this是什么,我们查看MDN对于onclick的详解:

image.png

所以,此时的this不再是view对象了,取而代之为这个dom对象。可是要怎样才能达到我们的初衷呢。最常见的方法是保存一个“假”的this,作为外部对象view的引用

var view= {
    element: 'div',
    bindEvent: function() {
        let _this = this;   // 保存外部this
        this.element.onclick = function() {
            _this.emitClick()  //拿到外部对象的引用
        }
    },
    emitClick: function() {
        this.element.addClass('active')
    }
}

这种方式可以完美的达到效果,但为了达到效果,让使用者对this的理解更加难,这样写甚至不如直接改成如下:

var view= {
    element: 'div',
    bindEvent: function() {
        this.element.onclick = function() {
            view.emitClick()  // 这种写法比较生硬
        }
    },
    emitClick: function() {
        this.element.addClass('active')
    }
}

以上两种写法都可解决问题,但都有缺点,结合上节对call的理解,为什么不能再调用函数时指定它的作用域呢?

// 伪代码(先不考虑this的情况)
...
this.element.onclick = function() {  
    this.emitClick()  
}
...


//以上虽然写了很多,但其实相当于
this.element.onclick = this.emitClick()
// 而包裹一层function的意义是给我们修改this的机会,因为以上this指向的是dom对象


// 结合call的用法,正确的代码是这样的
bindEvent: function(){
      let _this = this;
      this.element.onclick = function() {  
          _this.emitClick.call(_this)
      }
}


//但如果能不用包裹这层function那岂不是。。。美滋滋?
// bind这就应运而生了(我猜的)
bindEvent: function(){
      let _this = this;
      this.element.onclick = this.emitClick.bind(this)  //前边说过了,bind只是装饰了函数运行的环境,并没有执行(因为这里是点击后才执行)
}
emitClick: function() {
      this.element.addClass('active')
}

那么,bind到底做了什么呢?bind没有执行原函数,而是装饰了原函数并且返回一个新函数

Function.prototype.bind = function(){
    let _this = this; //拿到原函数
    let _self = arguments.shift();  // 拿到我们指定的this环境
    let arg = [].slice.call(arguments);  // 在bind时,还可以传递一些别的参数,这些参数可以被返回的函数使用,这里使用call,是因为“arguments”对象不是数组,只是类数组对象,没有slice方法
    return function(){
        let args = [].concat.call(arg, [].slice.call(arguments)); //在调用新函数时还可以传参数,将给新函数传的参数和bind时传递的参数合并起来
        return _this.apply(_self, args)
    }
}

铁子,我懂了,你呢?

相关文章

网友评论

    本文标题:bind方法的实现及原理

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