前几天,在 Twitter 看到一句非常整洁的代码:
var bind = Function.prototype.call.bind(Function.prototype.bind);
第一次看的时候,我可以知道这段代码想干嘛,它让 x.y(z) 变成 y(x,z). 带着非常愉快的心情,我把这段代码给我的学生们看,但是,他们问我这段代码干嘛用呢。然后我就开始解释,但是我不知道从何说起,然后我就转身走开了。
应该明白的是,大部分比较好的代码,应该直接告诉我们它们是用来做什么的,带着这些在函数式 JavaScript 领域的经验,并且也在不断的阅读函数式 JavaScript 相关的书籍,在读的时候,我觉得是没有任何问题的,但是解释,将这些函数式程序解释给别人来说,还是没有太多经验。
但是,我还是想尝试下,通过我的方式,一些简单的示例,还有大量的注释,来解释一下这段代码的意思。
定义一个简单的对象 context
var context = {foo : “bar”};
定义一个函数,使用了一个在 this 上下文中,被命名为 foo 的变量,
function returnFoo(){
return this.foo;
}
这个变量不存在于作用域中,因此,其返回 undefined。
(具体解释为,此时, this 指代全局的 window,而 window.foo 没有定义,所以返回 undefined)
returnFoo(); // undefined
但是,如果我们将此函数绑定到某个上下文。
var bound = returnFoo.bind(context);
现在变量 foo 存在于作用域中了.
(即,在 context 这个上下文中,是有定义 foo 变量的)
bound(); // bar
这就是 Function.prototype.bind 的作用,因为 returnFoo 是个函数,所以其继承了 function 的 prototype。
除了这种方式,还有另外其他方式来改变一个函数的上下文。例如 call 和 apply。
returnFoo.call(content); // bar
returnFoo.apply(content); // bar
还有就是将函数附加给某个对象,作为某个对象的一个方法。
content.returnFoo = returnFoo;
content.returnFoo(); // bar
让我们再来看看其他一些使用方法
Array.prototype 有一个特别好用的方法,叫做 slice。
在数组上调用此方法,它会返回一个从开始到结束的这个数组的一个拷贝,例如:
[1,2,3].slice(0,1); // [1]
现在将,slice 赋值给一个本地变量。
var slice = Array.prototype.slice;
slice 现在未被绑定到任何对象,所以它和 Array.prototype.slice 比起来,对提供给它的上下文,或者 this,是不能正常的工作。
slice(0,1); //typeError
slice([1,2,3],0,1); //TypeError
但是,如果我们通过 call 或者 apply 来调用它,并且给它提供一个上下文。
slice.call([1,2,3],0,1); // [1]
slice.apply([1,2,3],[0,1]); //[1]
那么对于 bind 呢?我们来试试。
slice = Function.prototype.call.bind(Array.prototype.slice);
slice([1,2,3],0,1); //[1]
很酷吧,那么我们对 bind 使用和 slice 一样的方式。
var bind = Function.prototype.call.bind(Function.prototype.bind);
var content = {foo : ”bar”};
function returnFoo(){
return this.foo;
}
var amazing = bind(returnFoo, content);
amazing(); //bar
这样,就明白了 这段代码的意图。
这个bind 的解释是来源于 MDN 的,但是不明白为什么 Google 对 MDN 的东西这么低的评价,所以要翻页才能找到的。