使用bind方法提取具有确定接收者的方法

来源:互联网 发布:七年级英语磁带mp3淘宝 编辑:程序博客网 时间:2024/05/22 10:35

由于方法与值为函数的属性没有区别,因此很容易提取对象的方法并将提取出的函数作为回调函数直接传递给高阶函数。但这也很容易忘记将提取出的函数的接收者绑定到该函数被提取的对象上。假设一个字符串缓冲对象使用数组来存储字符串,该数组稍后可能被连接起来。

var buffer  ={entries:[],add:function(s){this.entries.push(s);},concat:function(){return this.entries.join("")}};
似乎通过提取buffer对象的add方法,并使用ES5提供的forEach方法在每一个源数组元素上重复地调用add方法,就可以复制字符串数组到buffer对象中。

var source =["867","-","5309"];source.forEach(buffer,add);//error:entries is undefined
但buffer.add的接收者并不是buffer对象。函数的接收者取决于它是如何被调用的,然而,我们并没有在这里调用它。相反,我们把它传递给了forEach方法,而我们并不知道forEach方法在哪里调用了它。事实上,forEach方法的实现使用全局对象作为默认的接收者。由于全局对象没有entries属性,因此这段代码抛出了一个错误。幸运的是,forEach方法允许调用者提供一个可选的参数作为回调函数的接收者。所以,我们可以很轻松的修复该例子。

var source =["867","-","5309"];source.forEach(buffer,add,buffer);buffer.join();//"867-5309"
并非所有的高阶函数都细心周到地为其使用者提供其回调函数的接收者。如果forEach方法不接受额外的接受者参数怎么办?一个好的解决办法是创建使用恰当的方法调用语法来调用buffer.add的一个局部函数。

var source =["867","-","5309"];source.forEach(function(s){buffer.add(s);});buffer.join();//"867-5309"
该版本创建一个显式地以buffer对象方法 的方式调用add的封装函数。请注意该封装函数自身本身根本没有引用this变量。不管封装函数如何被调用,它总能确保将其参数推送到目标数组中。

创建一个函数用来实现绑定其接收者到一个指定的对象是非常常见的,因此ES5标准库中直接支持这种模式。函数对象的bind方法需要一个接收者对象,并产生一个以该接收者对象的方法调用的方式调用原来的函数的封装函数。使用bind方法,我们可以简化该例子:

var source =["867","-","5309"];source.forEach(buffer,add,bind(buffer));buffer.join();//"867-5309"
buffer.add.bind(buffer)创建了一个新的函数而不是修改了buffer.add函数。新函数的行为就像原来函数的行为,但它的接收者绑定到了buffer对象,而原有函数的接收者保持不变。

buffer.add===buffer.add.bind(buffer);//false

这很微妙但至关重要。这意味着调用bind方法是安全的,即使是一个可能在程序的其他部分被共享的函数。这对于原型对象上的共享方法尤为重要。当在任何的原型后代中调用共享方法时,该方法仍能正常工作。

总结:提取一个方法不会将方法的接收者绑定到该方法的对象上。

当给高阶函数传递对象方法时,使用匿名函数在适当的接收者上调用该方法。

使用bind函数创建绑定到适当的接收者函数。