最近面头条的时候,要求自己手动实现一个bind函数,然后又问了bind还能干嘛。这里就围绕这两个好好写一下。
首先bind的文档说明:
bind()方法创建一个新的函数, 当被调用时,将其this关键字设置为提供的值,在调用新函数时,在任何提供之前提供一个给定的参数序列。
fun.bind(thisArg[, arg1[, arg2[, ...]]])
参数
- thisArg
当绑定函数被调用时,该参数会作为原函数运行时的 this 指向。当使用new 操作符调用绑定函数时,该参数无效。 - arg1, arg2, ...
当绑定函数被调用时,这些参数将置于实参之前传递给被绑定的方法。 - 返回值
返回由指定的this值和初始化参数改造的原函数拷贝
bind() 函数会创建(返回)一个新函数(称为绑定函数),新函数与被调函数(绑定函数的目标函数)具有相同的函数体(在 ECMAScript 5 规范中内置的call属性)。当新函数被调用时 this 值绑定到 bind() 的第一个参数,该参数不能被重写。绑定函数被调用时,bind() 也接受预设的参数提供给原函数。(注意这句话是重点,也就是说bind支持了这个功能)
一般新手分不清于call,apply的关系,call/apply一般会伴随这函数的调用的,而bind只是返回了带新this的函数,将在下次调用。一般用法是调用bind后赋给一个变量,支持调用的时候继续传参。
我们一般快速实现一个bind:
/*
函数体内的this,就是需要绑定this的实例函数,或者说是原函数。最后我们使用apply来进行参数(context)绑定,并返回。
同时,将第一个参数(context)以外的其他参数,作为提供给原函数的预设参数,这也是基本的柯里化基础。(应该是偏函数)
*/
Function.prototype.bind = Function.prototype.bind || function(context) {
var self = this;
arg = Array.prototype.slice.call(arguments,1);
return function() {
return self.apply(context,arr);
}
}
但是这个实现有个问题,我们将参数限定了arguments.slice(1),我们返回的绑定函数中,如果想实现预设传参,上个代码就不能满足了。
eg:
function sum(a, b) {
console.log(a + b)
}
var sum2 = sum.bind(null,2); // 固定参数a, 值为2
sum2(4) // 传入参数b, 值为4, 结果为6 重在我们在调用函数的时候可以预设传参
那么我们将上个bind的实现更完善一下:
Function.prototype.bind = Function.prototype.bind || function(context) {
var self = this;
var args = Array.prototype.slice.call(arguments,1);
return function() {
var innerArgs = Array.prototype.slice.call(arguments);
var FinalArgs = args.concat(innerArgs);
return self.apply(context,FinalArgs);
}
}
这样就是实现了偏函数功能,在一些资料里是说“柯里化”,我觉得还是有点区别的,共同特点是实现了参数复用。
但举个例子:
假设有一个Add(x,y,z)函数,接收x,y,z三个参数,返回x+y+z
- 偏函数
AddBySeven =Otherbind(Add, 7);
AddBySeven(5, 10); // returns 22;
这是偏函数,固定了你函数的某一个或几个参数,返回一个新的函数,接收剩下的参数, 参数个数可能是1个,也可能是2个,甚至更多。
- 柯里化:把一个有n个参数的函数变成n个只有1个参数的函数
curryAdd = Curry(Add);
AddBySeven = curryAdd(7);
AddBySeven(5)(10); // returns 22
// curryAdd(7)(5)(10)
Add = (x, y, z) => x + y + z
变成了CurryAdd = x => y => z => x + y + z
很多资料有的叫柯里化有的叫偏函数,这点我觉得还是读者自己判断把。
到这里可能大家觉得上面的实现已经完美了,但是JS的坑是补不完的,问题又来了!
看过文档的就知道,在文档上介绍bind时还说了这点:
一个绑定函数也能使用new操作符创建对象:这种行为就像把原函数当成构造器。提供的 this 值被忽略,同时调用时的参数被提供给模拟函数。
那么如果bind返回的函数当做构造函数调用的时候,我们就需要在内部重新构造原型链了。所以更兼容的写法来了:
Function.prototype.bind = Function.prototype.bind || function(context) {
var self = this;
var args = Array.prototype.slice.call(arguments,1);
var Fun = {};
Fun.prototype = this.prototype;//继承原来函数
var Comb = function(){
var innerArgs = Array.prototype.slice.call(arguments);
var FinalArgs = args.concat(innerArgs);
return self.apply(this instanceof Fun ? this : context || this ,FinalArgs);
}
Comb.prototype = new Fun();
return Comb;
}
现在,
这里我有个问题,我明天好好问下师傅,懂了那个之后在这里继续说明,23333。