今天在网上看到了一个有意思的js题目,就拿去和同事讨论。本来以为是一个很简单的问题,但越讨论越深入,逐步认识到了这个问题的深度。
题目是这样的:
function f1(){
alert(1);
}
function f2(){
alert(2);
}
var f3 = f1.call;
f3.call(f2);
讨论的过程就不在赘述了,最后的结论是:
1 Function.prototype.call 实现的时候与是依赖与this的,如果直接调用f3(),浏览器将会报错。这个特性跟document.getElementById类似,比如在FF下,$=document.getElementById,调用$时浏览器会报错。
2 同样的对函数实例的call调用,如Function.prototype.call()与Function.prototype.call.call(Function.prototype与Function.prototype.call都是Function的实例),Function.prototype.call()可以正常调用,Function.prototype.call.call()则会抛出异常。
3 结合结论2以及题目本身,猜测是js引擎对call做了不同的实现,伪码如下:
var call = function(a,b,c){
this(b,c);//忽略this内部实现与a的绑定;
}
call.call = function(a,b,c){
a(b,c);//忽略this的绑定
}
Function.prototype.call = call;
这里猜测的是call本身作为Function的一个实例,在对call.call调用的时候,它的实例属性覆盖了它的原型属性。
4 但是通过比对 Function.prototype.call === Function.prototype.call.call 是为true的,所以结论3是错误的。应该是js内部为call的实现做了一个统一的托管,根据调用对象的不同,实现不同的逻辑。伪码如下:
function call(a,b,c){
if(this === call){
a(b,c);
}else{
this(b,c);
}
}
Function.prototype.call = call;
以上代码都最大化的做了简化,忽略了this的绑定以及参数的传递,只为了简单说明,请勿深究。有不妥的地方,欢迎拍砖。
其实这个结论4也不太准确,在我的另一篇博文http://rt0d.iteye.com/blog/1003754中,给出了证明。