Function.prototype.call2 = function(context) {
context.fn = this;
var args = [];
for(var i = 1, len = arguments.length; i < len; i++) {
args.push('arguments[' + i + ']');
}
var result = eval('context.fn(' + args +')');
delete context.fn; //删除挂载在借用对象上的fn属性
return result;
}
是为了模拟call的实现,请问为什么要push一个字符串,下面再用eval?直接传入arguments[i],然后下面用context.fn(args)为什么不可以?
这里我相信你也已经明白了
call
的原理,这里我简要还是说明一下原理,我也是参考JavaScript
权威指南的说明然后用代码实现的。首先我们看看
call
的语法和定义,来自ECMAScript规范中文版:还是简单举个栗子吧:
然后看看使用
call
之后的输出:下面我结合栗子来解答你的疑问:
首先你要明白上面模拟的函数对应栗子中变量的关系:
注意到这一步,我们只是把
jawil.sayHello
的引用地址给了lulin.fn
原来
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
要重组参数,你不能拿到一个参数执行一次函数吧,或者把参数存到一起一次执行吧,唯一的想到的做法就是把所有参数拼成字符串,然后用eval
执行,类似这种:“lulin.sayHello(arg1,arg2,arg3,arg4)”这才是我们想要的方式,而不是
lulin.sayHello([arg1,arg2,arg3,arg4])
,也不是lulin.sayHello(arg1)
,lulin.sayHello(arg2)
...什么是eval,这里也简单说一下,我就当你什么也不知道。
先简单了解一下
eval
函数吧定义和用法
语法:
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
等于多少?等于
"jawil1,23,43"
其实这个相当于
'jawil'+[1,2].toString()+[3,4].toString()+3
篇幅有限,更多隐式转换,请参考我的这篇文章:从++[[]][+[]]+[+[]]==10?深入浅出弱类型JS的隐式转换
说到这里,我把核心的都说了,你自己好好领悟领悟,原作者写这个东西估计也是参看别人的,很多东西没讲清楚,估计由于篇幅有限,所以一笔带过,看似很短的一段代码,其实包含了不少知识点的。