JS基础面试题之手写bind
- 手写bind
- 返回函数的模拟实现
- 传参的模拟实现
- 构造函数效果的模拟实现
- 构造函数效果的优化实现
- 最终版
手写bind
bind()方法会创建一个新的函数。当这个函数被调用时,bind()的第一个参数将作为它的运行时的this,之后的一序列参数将会在传递的实参前传入作为它的参数。
由此,我们可以首先得出bind函数的两个特点:
- 返回一个函数。
- 可以传入参数。
返回函数的模拟实现
var foo = {
value:1
};
function bar(){
console.log(this.value);
}
//返回一个函数
var bindFoo = bar.bind(foo);
bindFoo();//1
关于指定this的指向,我们可以使用call
或者apply
实现。
//第一版
Function.prototype.bind2 = function (context){
var self = this;
//考虑到绑定函数可能是有返回值的,加上return
return function(){
return self.apply(context);
}
}
传参的模拟实现
接下来,关于参数的传递:
var foo = {
value:1
};
function bar(name,age){
console.log(this.value);
console.log(name);
console.log(age);
}
var bindFoo = bar.bind(foo,'kin');
bindFoo('18');
//1
//kin
//18
当需要传name和age两个参数时,可以在bind的时候,只传一个name,在执行返回的函数的时候,再传另一个参数age。
这里如果不适用rest,使用arguments进行处理:
//第二版
Function.prototype.bind2 = function(context){
var self = this;
//获取bind2函数从第二个参数到最后一个参数
var args = Array.prototype.slice.call(arguments,1);
return function(){
//这个时候的arguments是指bind返回的函数传入的参数
var bindArgs = Array.prototype.slice.call(arguments);
return self.apply(context,args.concat(bindArgs));
}
}
构造函数效果的模拟实现
bind
还有一个特点,就是
一个绑定函数也能使用new操作符创建对象:这种行为就像把原函数当成构造器。提供的this值被忽略,同时调用时的参数被提供给模拟函数。
也就是说当bind
返回的函数作为构造函数的时候,bind
指定的this值会失效,但传入的参数依然生效。举个例子:
var value = 2;
var foo = {
value:1
};
function bar(name,age){
this.habit = 'shopping';
console.log(this.value,name,age);
}
bar.prototype.friend = 'ming';
var bindFoo = bar.bind(foo,'hong');
var obj = new bindFoo('18');
//undefined
//hong
//18
console.log(obj.habit);
console.log(obj.friend);
//shopping
//ming
尽管在全局和foo中都声明了value值,最后依然反悔了undefined,说明绑定的this失效了。
后文中new的模拟实现,就会知道这个时候的this已经指向了obj。
构造函数效果的优化实现
但是在这个写法中,我们直接将fBound.prototype = this.prototype
,我们直接修改fBound.prototype
的时候,也会直接修改绑定函数的prototype。这个时候,我们可以通过一个空函数来进行中转:
//第四版
Function.prototype.bind2 = function(context){
var self = this;
var args = Array.prototype.slice.call(arguments,1);
var fNOP = function(){};
var fBound = function(){
var 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;
}
最终版
调用bind的不是函数时,提示错误:
if(typeof this !== "function"){
throw new Error("Function.prototype.bind - what is trying to be bound is not callable");
}
最终代码为:
Function.prototype.bind2 = function (context){
if(typeof this !== "function"){
throw new Error("Function.prototype.bind - what is trying to be bound is not callable");
}
var self = this;
var args = Array.prototype.slice.call(arguments,1);
var fNOP = function(){};
var fBound = function(){
var 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.myBind = function(context){
//判断是否是undefined和null
if(typeof context === 'undefined' || context === null){
context = window;
}
self = this;
return function(...args){
return self.apply(context,args);
}
}
好啦!说实话,写到这里我也还有点蒙,我先消化一下~
欢迎大家留言讨论!一起消化!