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的详解:
所以,此时的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)
}
}
铁子,我懂了,你呢?