您的位置:首页 > Web前端 > JavaScript

弄明白backbone.js中的bind和bindAll方法

2013-09-16 17:07 537 查看
这些天在捣鼓backbone.js,总是不明白_.bindAll(this)是什么意思,后来看了一篇文章,觉得写得很清楚明白,于是试着翻译了一下,英文水平很烂,如果看得辛苦请直接看原文吧,最后有原文的链接。


Backbone.js的使用者会经常用到ucderscore.js提供的bind及bindAll方法。在这个博客中的我将要讨论为什么需要这些方法和它们是怎么工作的。

一切从apply方法开始

BindAll方法的内部使用了bind方法,而bind方法内部使用了apply方法,所以理解apply方法做了什么很重要。

var func = function beautiful(){
alert(this + ' isbeautiful');
};
func();


运行上面的代码我得到的结果是“[objectwindow] is beautiful”,得到这个结果是因为func方法被调用时this指的是window,它是默认的全局对象。

我们可以使用下面apply方法去改变this这个值。

var func = function beautiful(){
alert(this + ' isbeautiful');
};
func.apply('Internet');


上面的例子运行的结果是“Internetis beautiful”,类似地下面的代码得到的结果是“Beachis beautiful”。

var func = function beautiful(){
alert(this + ' isbeautiful');
};
func.apply('Beach'); //Beach is beautiful


总之,当调用function时使用apply方法可以让修改this的值。

为什么需要bind方法

为了弄清楚为什么需要bind方法,让我们先看看下面的例子。

function Developer(skill) {
this.skill = skill;
this.says =function(){
alert(this.skill + ' rocks!');
}
}
var john = new Developer('Ruby');
john.says(); //Ruby rocks!


上面的例子非常的直观,john是Developer的一个实例,当says方法被调用时我们得到了正确的结果。

注意我们调用says方法时我们是john.says()这样调用的。如果我们只是通过一个返回says方法的句柄去调用john.says,那么上面的代码就可以写成下面那样。

function Developer(skill) {
this.skill = skill;
this.says =function(){
alert(this.skill +' rocks!');
}
}
var john = new Developer('Ruby');
var func = john.says;
func();// undefined rocks!


上面的代码和前一段代码很像。我们改变的就只是把says方法用一个叫func的变量存起来了。如果调用这个方法应该会得到我们想要的结果,然而我们运行代码时却得到了“undefinedrocks!”。

我们得到的结果是“undefinedrocks!”,这是因为在这个例子中func是被全局的上下文调用的,当func被运行时this是被指向windw的。在window中没有任何叫做skill的属性的,因此this.skill输出就是undefined。

前面我们说过使用apply可以修正因this产生的问题,让我们尝试一下使用apply去修正上面的问题。

function Developer(skill) {
this.skill = skill;
this.says =function(){
alert(this.skill +' rocks!');
}
}
var john = new Developer('Ruby');
var func = john.says;
func.apply(john);


上面的代码修正了我们的问题。这次我们得到的结果是“Rubyrocks!”,但这里却有一个大问题。

在javaScript的世界里functions是很高级的。我们之所以创建functions是因为我们能很容易地去使用它。在上面的例子中我们创建了一个叫func的方法。然而跟随着func方法我们还要一直保持着john的传递。这不是一个好事情。第二点是正确调用这个方法的责任已经由方法的创建者转移到了方法的使用者了。这不是一个好的API。

我们应该尝试创建很容易被使用者调用的方法。这就是bind方法诞生的原因。

Bind是怎么解决问题的

首先让我们看看如何使用bind去解决问题的。

function Developer(skill) {
this.skill = skill;
this.says =function(){
alert(this.skill +' rocks!');
}
}
var john = new Developer('Ruby');
var func = _.bind(john.says, john);
func();// Ruby rocks!


为了解决这个问题,我们需要一个已经映射到john的function,这样的话我们就不需要一直带上john了。这恰好就是bind所做的。它返回了一个新的方法且这个新的方法已经绑定到我们所提供的值里面。

这是bind方法实现的一个片段代码

return function() {
returnfunc.apply(obj, args.concat(slice.call(arguments)));
};


就像你看到的那样,当调用bind方法时bind内部使用了apply方法把this设置到我们传过去的第二个参数里面。

注意bind方法没有修改已经存在的方法。它返回了一个新的方法,那个新的方法才是被使用的。

BindAll是怎么解决问题的

除了bind我们也可以使用bindAll。下面是用bindAll的解决方案。

function Developer(skill) {
this.skill = skill;
this.says =function(){
alert(this.skill +' rocks!');
}
}
var john = new Developer('Ruby');
_.bindAll(john, 'says');
var func = john.says;
func(); //Ruby rocks! 


上面的代码与bind的解决方案很相似但却有很大的不同。

第一个不同点是我们不用关心bindAll的返回值。在bind的例子中我们必须使用它返回的function。在bindAll的例子中我们不用关心返回值但要付出一点代价,bindAll把function改变了。

John对象一个返回function的属性叫做says,bindAll把这个says属性改变了才能使它返回的function已经绑定到了john对象。

这里是bindAll方法实现的一片段代码。

function(f) { obj[f] = _.bind(obj[f], obj); }

注意到bindAll内部调用了bind方法,它用bind返回的function重载了已经存在的属性。

Bind和bindAll的另外一个不同点是bind方法第一个参数是functionjohn.says,第二个参数是john这个值。在bindAll方法中第一个参数是john这个值,第二个参数并不是一个function而是属性名。

需要注意的事

当使用backbone.js使用应用时某人写了这样的糟糕代码

window.ProductView = Backbone.View.extend({
initialize:function() {
_.bind(this.render, this);
this.model.bind('change',this.render);
}
}); 


上面的代码不可以正常运行因为bind的返回值没有被使用,正确的用法应该是这样的

window.ProductView = Backbone.View.extend({
initialize:function() {
this.model.bind('change', _.bind(this.render, this));
}
});


或者可以像下面那样使用bindAll。

window.ProductView = Backbone.View.extend({
initialize:function() {
_.bindAll(this,this.render);
this.model.bind('change', this.render);
}
});


翻译得很烂?呵呵,看原文吧:http://blog.bigbinary.com/2011/08/18/understanding-bind-and-bindall-in-backbone.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息