Function.prototype.call2 = function(context) {
context.fn = this;
var args = [];
for(var i = 1, len = arguments.length; i < len; i++) {
args.push('arguments[' + i + ']');
}
eval('context.fn(' + args +')');
delete context.fn;
}
是为了模拟call的实现,请问为什么要push一个字符串,下面再用eval?直接传入arguments[i],然后下面用context.fn(args)为什么不可以?
这里我相信你也已经明白了
call
的原理,这里我简要还是说明一下原理,我也是参考JavaScript
权威指南的说明然后用代码实现的。首先我们看看
call
的语法和定义,来自ECMAScript规范中文版:还是简单举个栗子吧:
然后看看使用
call
之后的输出:下面我结合栗子来解答你的疑问:
首先你要明白上面模拟的函数对应栗子中变量的关系:
注意到这一步,我们只是把
jawil.sayHello
的引用地址给了lulin.sayHello
原来
jawil.sayHello.call(context,arg1,arg2,arg3,arg4)
按照你的方式剔除
context
得到args=[arg1,arg2,arg3,arg4]
然后执行
lulin.sayHello([arg1,arg2,arg3,arg4])
哈哈,很有迷惑性是不是,看着没问题,其实已经变味了,原来是原来是四个参数,现在集合到一个数组就是一个数组参数了,问题就出在这里。那么怎么解决这个问题,思路就是上面那样,把所有参数拼成字符串,然后用
eval
执行。我们想要的效果是
吧,或者lulin.sayHello(arg1,arg2,arg3,arg4)
这样的,因为lulin.sayHello
要重组参数,你不能lulin.sayHello(arg1,arg2,arg3,arg4)
这样的,因为lulin.sayHello
要重组参数,你不能拿到一个参数执行一次函数吧,或者把参数存到一起一次执行吧,唯一的想到的做法就是把所有参数拼成字符串,然后用eval
拿到一个参数执行一次函数把参数存到一起一次执行
执行,lulin.sayHello([arg1,arg2,arg3,arg4])
,也不是lulin.sayHello(arg1)
,lulin.sayHello(arg2)
吧,唯一的想到的做法就是把所有参数拼成字符串,然后用,也不是
lulin.sayHello(arg1)
,lulin.sayHello(arg2)
...eval
什么是eval,这里也简单说一下,我就当你什么也不知道。
eval() 函数可计算某个字符串,并执行其中的的 JavaScript 代码。
eval(string)
简单来说吧,就是用JavaScript的解析引擎来解析这一堆字符串里面的内容,这么说吧,你可以这么理解,你把
eval
看成是<script>
标签。就是相当于这样
好了,我们再来看上面那段代码,其实还有坑的,来看看调式直观点。下面是完整的调式代码:
看args的输出:
["arguments[1]", "arguments[2]"]
然后再看 'context.fn(' + args + ')'的输出:
"context.fn(arguments[1],arguments[2])"是不是有点懵逼
其实这里涉及到了隐式转换,举个栗子吧:
其实这个相当于'jawil'+[1,2]+[3,4]+3
等于多少?'jawil'+[1,2]+[3,4]+3
等于多少?等于
"jawil1,23,43"
其实这个相当于
'jawil'+[1,2].toString()+[3,4].toString()+3
等于"jawil1,23,43"
'jawil'+[1,2].toString()+[3,4].toString()+3
args 是一个数组,
context.fn(args)
只有一个参数,正常情况下可以用 apply 可以解决数组转参数,但这里模拟 call ,用 apply 就没意思了。所以它利用了数组的 toString() 把 context 以外的参数搬到 context.fn 上。因为 arguments[0] 是 context
你没看循环变量从1开始的么!