JavaScript中的bind方法及其常见应用
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
JavaScript中的bind⽅法及其常见应⽤
⼀、bind()⽅法的实现
在JavaScript中,⽅法往往涉及到上下⽂,也就是this,因此往往不能直接引⽤。
就拿最常见的console.log("info…")来说,避免书写冗长的console,直接⽤log("info…")代替,不假思索的会想到如下语法:
var log = console.log;
log("info");
很遗憾,运⾏报错:TypeError: Illegal invocation。
原因很清楚:对于console.log("info…")⽽⾔,log⽅法在console对象上调⽤,因此log⽅法中的this指向console对象,⽽我们⽤log变量指向console.log⽅法,然后直接调⽤log⽅法,此时log⽅法的this指向的是window对象,上下⽂不⼀致,当然会报错了。
此时我们可以⽤bind⽅法解决这个问题。
bind⽅法允许⼿动传⼊⼀个this,作为当前⽅法的上下⽂,然后返回持有上下⽂的⽅法。
例如:
var write = document.write.bind(document);
write("hello");
这样就不会报错了。
但是,bind⽅法并不⽀持ie 8以及更低版本的浏览器,我们完全可以⾃⼰实现⼀个,很简单。
Function.prototype.bind = Function.prototype.bind || function(context){
var _this = this;
return function(){
_this.apply(context, arguments);
};
};
核⼼就是通过apply⽅法实现,闭包的经典应⽤。
_this指向当前⽅法,context指向当前⽅法的上下⽂,⼆者均通过闭包访问。
bind所做的就是⾃动封装函数在函数⾃⼰的闭包中,这样我们可以捆绑上下⽂(this关键字)和⼀系列参数到原来的函数。
你最终得到的是另⼀个函数指针。
function add(a,b){
return a + b;
}
var newFoo = add.bind(this,3,4);
请注意,我们不仅捆绑this到newFoo()函数,⽽且我们也捆绑了两个参数。
所以,当我们调⽤newFoo()的时候,返回值将是7。
但是,如果我们在调⽤之前newFoo更改的参数的话,会发⽣什么?
如果我们使⽤变量绑定参数到newFoo(),然后在调⽤newFoo()前改变变量,你觉得值会变为什么呢?
function add(a,b){
return a + b;
}
var a = 3;
var b = 4;
var newFoo = add.bind(this,a, b);
a = 6;
b = 7;
console.log(newFoo());
返回值仍然是7,因为bind()绑定的是参数的值,⽽不是实际变量的值。
这是好消息,我们可以在代码中利⽤这个巨⼤的优势。
⼆、bind()的应⽤:
1、绑定函数的this值
bind()最简单的⽤法是创建⼀个函数,使这个函数不论怎么调⽤都有同样的this值。
常见的错误就像上⾯的例⼦⼀样,将⽅法从对象中拿出来,然后调⽤,并且希望this指向原来的对象。
如果不做特殊处理,⼀般会丢失原来的对象。
使⽤bind()⽅法能够很漂亮的解决这个问题:
this.num = 9;
var mymodule = {
num: 81,
getNum: function() { return this.num; }
};
module.getNum(); // 81
var getNum = module.getNum;
getNum(); // 9, 因为在这个例⼦中,"this"指向全局对象
// 创建⼀个'this'绑定到module的函数
var boundGetNum = getNum.bind(module);
boundGetNum(); // 81
改变对象⽅法⾥this的值
改变事件处理函数⾥的this值,因为在事件处理函数中的this指向的是dom元素,在某些情况下我们需要改变这个this值
2、偏函数实现
截取⼀段关于偏函数的定义:
Partial application can be described as taking a function that accepts some number of arguments,
binding values to one or more of those arguments,
and returning a new function that only accepts the remaining, un-bound arguments.
这是⼀个很好的特性,使⽤bind()我们可以设定函数的预定义参数,然后调⽤的时候传⼊其他参数即可。
//使⽤bind,我们就可以像这样写代码实现Currying:
function add(a,b,c) {
return a+b+c;
}
var addAgain = add.bind(this, 1, 2);
var result = addAgain(3);
function list() {
return Array.prototype.slice.call(arguments);
}
var list1 = list(1, 2, 3); // [1, 2, 3]
// 预定义参数37
var leadingThirtysevenList = list.bind(undefined, 37);
var list2 = leadingThirtysevenList(); // [37]
var list3 = leadingThirtysevenList(1, 2, 3); // [37, 1, 2, 3]
3、在定时器中使⽤,⽐如和setTimeout⼀起使⽤
⼀般情况下setTimeout()的this指向window或global对象。
当使⽤类的⽅法时需要this指向类实例,就可以使⽤bind()将this绑定到回调函数来管理实例。
4、bind⽤于事件处理程序
当⼀个事件处理程序被调⽤时,它访问的上下⽂会⽣成事件,⽽不是在创建事件处理程序的对象中。
通过使⽤bind,可以肯定的是,函数会被访问正确的上下⽂。
三、多次绑定bind⽅法
如果使⽤bind()⽅法多次绑定,最终得到的this会是哪个绑定的呢?
function say() {
alert(this.x);
};
var a = say.bind({x: 1});
var b = a.bind({x: 2});
b(); // 这⾥会输出1还是2呢?
那么我们不妨分析⼀下:
//say函数使⽤bind⽅法,穿进去了⼀个对象,相当于
var a = function() {
return say.apply({x: 1});
};
//如果我们对得到的函数a再进⾏绑定,则相当于
var b = function() {
return a.apply({x: 2});
};
即
var b = function() {
return function() {
return say.apply({x: 1});
}.apply({x: 2});
};
这样虽然我们改变了函数a⾥this的值,但是最后函数say⾥的this的值还是由第⼀次绑定时的参数决定,⽽与函数a中的this值⽆关。
1、多次绑定的结果
所以⽆论使⽤bind绑定多少次,最终原函数的this值是由第⼀次绑定传的参数决定的。
2、多次绑定参数的顺序
function say() {
alert(this.x);
};
var a = say.bind({x: 1},1,2,3);
var b = a.bind({x: 2},4,5,6);
a(7,8,9);
b(7,8,9);
// 此时原函数say参数的顺序的怎样的呢?
// 是[4,5,6,1,2,3,7,8,9]还是[1,2,3,4,5,6,7,8,9]
⾸先对say使⽤bind⽅法,会改变函数say的this值,和“内置”参数。
所以 a(7,8,9) 的参数组成是:内置的参数 + 调⽤时传⼊的参数 = 最终函数,即[1,2,3]+ [7,8,9] = [1,2,3,7,8,9]
⽽对函数a使⽤bind⽅法,只会改变函数a的this值,和往函数a⾥“内置”参数。
所以 b(7,8,9) 的参数组成是:[1,2,3](在函数say内置的参数) + [4,5,6](在函数a内置的参数) + [7,8,9] = [1,2,3,4,5,6,7,8,9]
总结:对哪个函数使⽤bind()⽅法即改变这个函数的this值,和内置其参数,或者说像克⾥化⼀样理解,先预置好参数
var a = say.bind({x:1},1,2,3); // 是改变函数say的this值,和在函数say上预置参数1,2,3
var b = a.bind({x: 2}, 4,5,6); // 是改变函数a的this,和在函数a上预置预置参数4,5,6。