思考匿名函数的this context到底是什么的同时,找到了相当一部分资料,是教你如何绑定这个this的。他们都不约而同地使用了一个方法:Array.prototype.slice.call(arguments, 1),但是这又引起了另外一个问题,call方法的第一个参数,不是this对象吗?为什么可以传arguments?
问题
我们先来看看google出来的资料里面,如何自己实现一个bind方法的代码片段:
|
|
这样写的目的很明显,args就是this context以外的所有参数,形式为数组。
之前对call方法浅显的了解,就是它和apply方法一样,都可以方便地绑定this对象,并在接下来的参数中,传入原方法需要的其它参数。
就算是上面这个片段中,最后return的方法,apply方法的第一个对象,其实也是this对象啊。
但是为什么args这里可以直接传arguments进去呢?
在MDN-Function.prototype.call()上是这么写的:
fun.call(thisArg[, arg1[, arg2[, …]]])
看到这个写法,我觉得人家官方已经说得很明确,call方法的第一个参数,就一定个this。
接着查到,MDN-Arguments object简单带过了一下,说用下面这个方法就可以把本来不是Array的arguments对象,转化为Array:
var args = Array.prototype.slice.call(arguments);
当时我严重觉得MDN在arguments这里要么就是写错了,要么就是没说清楚具体的原理。
答案
于是我把自己能想到的关键字都组合了一遍,每次google出来的答案,我最少都要翻上三页,直到我认为出来的结果已经没有任何再dig的意义为止。
在折腾了一天以后,我找到了一篇文章,里面说的东西跟如何bind``this一点关系都没有,但是它写出了Array.prototype.slice.call的真正意思:
|
|
看到这行代码我就豁然开朗了。翻译成中文它是这个意思:
|
|
不是什么thisArg,不是什么“call和apply可以让你方便地绑定不同的this对象”,
而是,如果你想要指定某个方法的作用对象,那就使用call或apply方法吧(大多数情况下”作用对象”都是this)。
在stackoverflow上,有另外的解释说,这样做其实是强行把slice的this对象设置成了“拥有.length属性,且其它属性都是数字索引的形式”(has a numeric .length property, and a bunch of properties that are numeric indices)的对象,它依然能照常工作。
上面说的这种对象,有个名称叫Array-like object(我自己的翻译是,类数组对象)。有几种典型的对象属于这一类型:
|
|
再进一步
看多了下面这种写法:
|
|
我就会想,以slice方法威力,为什么不是直接:
|
|
这样呢?使用prototype里面的方法有什么好处么?
答案是:
如果直接用上面这两种方法,会首先创建一个Array对象。然而我们对这个对象并没有兴趣,所以根本不应该创建这么一个多余的对象。从Array的原型链上获取slice方法,是最直接的方式。
参考资料:
[1]. Javascript Tricks: Array.prototype.slice.call(arguments)
[2]. stackoverflow-how does Array.prototype.slice.call() work?
[3]. Converting objects to arrays using Array.prototype.slice.call()