搞懂JavaScript的Function.prototype.bind[译]
binding多是初学Javascript的人最不体贴的函数,当你意想到须要『坚持this在其他函数中的高低文』,实际上你须要的是Function.prototype.bind()。
你第一次碰到题目标时刻,你能够倾向于把this赋值给一个变量,你就能够在高低文转变的时刻,也能够运用。许多人挑选self,_this或许context来定名。这些都不会被用到,如许做也没什么题目,然则这里有更好的方法,特地处置惩罚这个题目。
我愿意为作用域做任何事,但我不会that = this
— Jake Archibald (@jaffathecake) February 20, 2013
我们真正在追求处置惩罚的题目是什么?
看看这段代码,把高低文赋值给一个变量:
var myObj = {
specialFunction: function () {
},
anotherSpecialFunction: function () {
},
getAsyncData: function (cb) {
cb();
},
render: function () {
var that = this;
this.getAsyncData(function () {
that.specialFunction();
that.anotherSpecialFunction();
});
}
};
myObj.render();
假如上面直接用this.specialFunction(),结果是一个错误信息:
Uncaught TypeError: Object [object global] has no method ‘specialFunction’
当回调的时刻,我们须要坚持myObj的高低文援用。运用that.specialFunction(),让我们用that的高低文且准确实行函数。但是,用Function.prototype.bind()能够简化一些。
重写例子:
render: function () {
this.getAsyncData(function () {
this.specialFunction();
this.anotherSpecialFunction();
}.bind(this));
}
我们刚做了什么?
.bind()就是创建了一个新函数,当我们呼唤时,把他的this赋值。所以我们能够通报我们的高低文,this(指向myObj),通报进.bind()函数。当回调实行的时刻,this指向myObj。
假如我们对Function.prototype.bind()的内部完成有兴趣,请看下面的例子:
Function.prototype.bind = function (scope) {
var fn = this;
return function () {
return fn.apply(scope);
};
}
一个简朴的例子:
var foo = {
x: 3
}
var bar = function(){
console.log(this.x);
}
bar(); // undefined
var boundFunc = bar.bind(foo);
boundFunc(); // 3
浏览器兼容性
Browser | Version support |
---|---|
Chrome | 7 |
Firefox (Gecko) | 4.0 (2) |
IE | 9 |
Opera | 11.60 |
Safari | 5.1.4 |
如你所见,不幸的是,不支持ie8以下(啥也不说了)。
荣幸的是,MDN为那些原生不支持.bind()的浏览器供应了处置惩罚:
if (!Function.prototype.bind) {
Function.prototype.bind = function (oThis) {
if (typeof this !== "function") {
// closest thing possible to the ECMAScript 5 internal IsCallable function
throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
}
var aArgs = Array.prototype.slice.call(arguments, 1),
fToBind = this,
fNOP = function () {},
fBound = function () {
return fToBind.apply(this instanceof fNOP && oThis
? this
: oThis,
aArgs.concat(Array.prototype.slice.call(arguments)));
};
fNOP.prototype = this.prototype;
fBound.prototype = new fNOP();
return fBound;
};
}
运用体式格局
进修东西时刻,我发明有用的体式格局不是仔细的去进修观点,而是去看怎样运用到如今的工作中。假如顺遂的话,下面某些例子能够被用到你的代码中处置惩罚你面临的题目。
点击事宜处置惩罚
个中一个用途是追踪点击(点击后实行一个行动),须要我们在一个对象中贮存信息:
var logger = {
x: 0,
updateCount: function(){
this.x++;
console.log(this.x);
}
}
我们写click事宜处置惩罚,然后呼唤logger中的updateCount():
document.querySelector('button').addEventListener('click',logger.updateCount);
但我们造了一个不必要的匿名函数,坚持this的准确指向。
简化一下:
document.querySelector('button').addEventListener('click', logger.updateCount.bind(logger));
适才我们用了.bind()制造一个新函数然后把作用域绑定到logger对象上。
时候距离函数
假如你之前用过模板引擎(handlebars)或许MV*框架,那你应当意想到一个题目标发作,当你呼唤衬着模板,马上想进入新的DOM节点。
假定我们尝试实例一个jQuery插件:
var myView = {
template: '/* a template string containing our <select /> */',
$el: $('#content'),
afterRender: function () {
this.$el.find('select').myPlugin();
},
render: function () {
this.$el.html(this.template());
this.afterRender();
}
}
myView.render();
你会发明这可用,但并不老是可用的。这就是题目所在。这就像是老鼠竞走:不论发作什么,第一个抵达获得胜利。有时刻是render,有时刻是插件的实例(instantiation)。
如今,一个不为人知,我们能够用小hack—setTimeout()。
须要重写一下,一旦Dom节点涌现,我们就能够平安的实例我们的JQuery插件。
//
afterRender: function () {
this.$el.find('select').myPlugin();
},
render: function () {
this.$el.html(this.template());
setTimeout(this.afterRender, 0);
}
//
但是,我们会看到.afterRender()没有被找到。
咋办?把我们.bind()加进去:
//
afterRender: function () {
this.$el.find('select').myPlugin();
},
render: function () {
this.$el.html(this.template());
setTimeout(this.afterRender.bind(this), 0);
}
//
如今afterRender()能够在准确的高低文中实行了。
整合事宜绑定和QUERYSELECTORALL
DOM API一个严重进步就是querySelector,querySelectorAll和classList API等等。
但是,并没有原生增加事宜到多个节点(nodeList)的体式格局。所以,我们终究偷盗了forEach函数,来自Array.prototype,以下:
Array.prototype.forEach.call(document.querySelectorAll('.klasses'), function(el){
el.addEventListener('click', someFunction);
});
更好一点,用.bind():
var unboundForEach = Array.prototype.forEach,
forEach = Function.prototype.call.bind(unboundForEach);
forEach(document.querySelectorAll('.klasses'), function (el) {
el.addEventListener('click', someFunction);
});
如今我们有了玲珑的方法来轮回多个dom节点。
总结
如你所见,.bind()函数能够用来完成种种目标,同时简化代码。愿望这个概述能让你的代码能运用.bind(),利用好变化的this这个特性。
『才能有限,若有疑问,马虎,速指出,谢谢你』