一句话介绍bind:
bind()方法会创建一个新函数。当这个新函数被调用时,bind()的第一参数将作为它允许时的this,之后的一序列参数将会在传递的实参前传入作为它的参数。(来自于MDN)
bind 函数的两个特点:
1、返回一个函数
2、可以传入参数
返回函数的模拟实现
// 第一版
Function.prototype.bind2 = function(context){
var self = this;
return function(){
return self.apply(context);
}
}
之所以 return self.apply(context),是考虑到绑定函数会有返回值,如:
let foo = {
value: 1
}
function bar(){
return this.value;
}
let bindFoo = bar.bind(foo);
console.log(bindFoo()); // 1
传参模拟实现
在使用bind时是否可以传参呢?执行bind返回的函数时是否可以传参?
let foo = {
value: 1
}
function bar(name,age){
console.log(this.value);
console.log(name);
console.log(age);
}
let bindFoo = bar.bind(foo,'cyl');
bindFoo('18');
// 1
// cyl
// 18
函数可以在bind的时候和执行返回的函数进行传参
// 第二版
Function.prototype.bind2 = function(context){
let self = this;
// 获取bind2函数 从第二个参数到最后一个参数
let args = Array.prototype.slice.call(arguments,1);
return function (){
//这里函数的arguments是指bind返回的函数入参
let bindArgs = Array.prototype.slice.call(arguments);
return self.apply(context, args.concat(bindArgs));
}
}
构造函数模拟实现
一个绑定函数也能使用new操作符创建对象:这种行为就像把原函数当成构造器。提供的this值被忽略,同时调用的参数被提供给模拟函数
当bind返回的函数作为构造函数时,bind时指定的this值会失效,但传入的参数依然生效。
let value = 2;
let foo = {
value: 1
}
function bar(name,age){
this.a = '12';
console.log(this.value);
console.log(name);
console.log(age);
}
bar.prototype.friend = 'cegz';
let bindFoo = bar.bind(foo,'cyl');
// new bindFoo时执行了bindFoo,但new将this指向了实例对象obj,
// 而obj并没有value这个属性,所以不会访问到bar和全局的value
let obj = new bindFoo('18');
// undefined
// cegz
// 18
console.log(obj.friend); // cegz
console.log(obj.a); // 12
要实现上面的功能,我们可以通过修改返回的函数的原型来实现
//第三版
Function.prototype.bind2 = function(context){
let self = this;
let args = Array.prototype.slice.call(arguments,1);
let fBound = function(){
let bindArgs = [... arguments];
//如果当前this为fBound的实例,表示是执行了new,指向this,否则指向bind对象
return self.apply(this instanceof fBound ? this : context, args.concat(bindArgs));
}
//修改返回函数的 prototype 为绑定函数的 prototype,new出实例对象就可以继承绑定函数的原型中的成员
fBound.prototype = this.prototype;
return fBound;
}
构造函数效果的优化实现
如果我们直接将fBound.prototype = this.prototype,我们直接修改 fBound.prototype的时候,也会直接修改绑定函数的prototype,这时候我们通过一个空函数进行中转
// 第四版
Function.prototype.bind2 = function(context){
let self = this;
let args = Array.prototype.slice.call(arguments,1);
let fNOP = function(){};
let fBound = function(){
let bindArgs = Array.prototype.slice.call(arguments);
return self.apply(this instanceof fNOP ? this : context, args.concat(bindArgs));
}
fNOP.prototype = this.prototype;
fBound.prototype = new fNOP();
return fBound;
}
最终代码
Function.prototype.bind2 = function(context){
if(typeof this !== 'function'){
throw new Error('Function.prototype.bind - what is trying to be bound is not callable');
}
let self = this;
let args = Array.prototype.slice.call(arguments,1);
let fNOP = function(){}
let fBound = function(){
let bindArgs = Array.prototype.slice.call(arguments);
return self.apply(this instanceof fNOP ? this : context, args.concat(bindArgs));
}
fNOP.prototype = this.prototype;
fBound.prototype = new fNOP();
return fBound;
}
网友评论