思考匿名函数的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()