函数绑定
咱们直接来看例子吧
未绑定
在上面这个例子中,创建了一个叫做 handler 的对象。handler.handleClick()方法被分配为
一个 DOM 按钮的事件处理程序。当按下该按钮时,就调用该函数,显示一个警告框。
虽然警告框应该显示 Event handled ,然而实际上显示的是 undefiend 。
这个问题就是没有保存 handler.handleClick()的环境,所以 this 对象最后是指向了 DOM 按钮而非 handler。
这就是绑定函数存在的意义,创建函数并保留代码的执行环境。
闭包
解决方案有,在 onclick 事件处理程序内使用了一个闭包直接调用 handler.handleClick()。就这一个闭包还行,如果创建多个闭包可能会令代码变得不好理解和调试,万一哪出了个bug呢。所以,很多JavaScript 库实现了一个可以将函数绑定到指定环境的函数。这个函数一般都叫 bind()。
bind()方法的语法
一个简单的 bind()函数接受一个函数和一个环境,并返回一个在给定环境中调用给定函数的函数, 并且将所有参数原封不动传递过去。语法如下:
它看着简单,功能还是挺强大的。在 bind()中创建了一个闭包,闭包使用 apply()调用传入的函数,并给 apply()传递 context 对象和参数。这个 arguments 对象是内部函数的,而非 bind()的。
bind() 实现函数绑定
当调用返回的函数时,它会在给定环境中执行被传入的函数并给出所有参数。 就这个例子,bind()函数创建了一个保持了执行环境的函数,并将其传给btn.addEventListener()。DOM中addEventListener() 方法用于向指定元素添加事件句柄。这样按钮就能成功指向handler了。
原生bind方法
handler.handleClick()方法和平时一样可以获得 里面 对象,因为所有的参数都通过被绑定的函数直接传给了它。也可以这样,你不用再自己定义 bind()函数了,而是可以直接在函数上调用这个方法。
拓展bind()语法
如果不能确定bind()方法是否存在,那么可以使用以下方法来绑定函数。如果bind()方法存在,使用bind()方法绑定函数并则返回新函数(fn.bind(context))如果bind()方法不存在,这样实现将fn()绑定到对象context上。
从以上可以看出,“fn.apply(context, argument)”的效果与“fn.bind( context )”的效果是一样的和原来执行的效果也相同,这样更加严谨些。
只要是将某个函数指针以值的形式进行传递,同时该函数必须在特定环境中执行(比如例子中的handler.handleClick(),被绑定函数的效用就突显出来了。它们主要用于事件处理程序以及 setTimeout() 和 setInterval()。
函数柯里化
函数柯里化的方法与函数绑定是一样的,唯一的区别在于 函数柯里化所创建的函数还需要传入一些参数。
函数柯里化的用途是指定函数的不变参数,使得多次调用函数的时候不用重复传入相同的参数,就像Java里面的static变量。
由apply()方法实现柯里化
动态创建柯里化函数:调用另一个函数并为它传入要柯里化的函数和必要的参数。
柯里化函数步骤:
第一步:柯里化函数,并返回一个新函数。var oldSum = curry(oldSum, 3)
1、args=[3]
,外部函数就是curry()
,且将第二个参数"3"保留在了数组args
中。我们知道arguments
对象可以访问传入到函数中的多个实参,这里就“3”这个实参保留在了args
数组中。
2、return function () {}
返回匿名函数
第二步:调用newSum(4)
1、innerArgs=[4]
,这里将内部函数newSum的参数"4"保留在innerArgs
中。
2、在外部、内部函数的参数数通过操作符concat
拼接成一个数组并保留在finalArgs
中。
3、fn.apply(null, finalArgs) ==> fn(finalArgs) ==> oldSum(3, 4)
最后得出结果为7
。
另外一些小知识:
prototype 属性使您有能力向对象添加属性和方法。
slice() 方法可从已有的数组中返回选定的元素。
concat() 方法用于连接两个或多个数组。
call() 方法是预定义的 JavaScript 方法。
它可以用来调用所有者对象作为参数的方法。
通过 call(),您能够使用属于另一个对象的方法。
call() 方法可接受参数。
两个外部函数参数
解析:
此时外部函数参数有两个分别为“4”和"6",通过新函数newSum()传入到原始函数oldSum()的实参有三个,但由于oldSum()只有两个形参,所以内部函数(也就是newSum)的实参被忽略,只传入给了原始函数“4”和“6”这两个参数。
由bind()方法实现柯里化
bind()方法不仅仅可以将函数绑定到一个对象上,它还有其它的作用:
除了第一个实参外,传入bind()方法的实参也会被绑定到this对象上,这里的this的值绑定到传入bind()方法的对象或函数上。
bind()方法实现柯里化的方法就是:在this的值后面再传入另一个参数即可。
更多:
传入bind()方法的实参优先绑定到绑定函数(相当于原始函数)的形参上。
如果传入bind()方法的实参个数多于绑定函数的形参个数,一一对应,则忽略最后的几个实参;
如果传入bind()方法的实参个数少于绑定函数形参的个数,则实参绑定到前几个形参,剩下的形参绑定新函数传入的实参,若此时新函数传入的实参多个剩下形参的个数,一一对应,则忽略新函数的最后几个实参。
最后,两者区别:
柯里化就是在绑定函数的基础上,在this值之后再传入实参。