当前位置: 代码迷 >> Web前端 >> Function.prototype.call 内部兑现探讨
  详细解决方案

Function.prototype.call 内部兑现探讨

热度:92   发布时间:2012-10-12 10:17:04.0
Function.prototype.call 内部实现探讨

今天在网上看到了一个有意思的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中,给出了证明。

1 楼 gr205925 2011-10-15  
function a(){
    alert(1);
}
function global(){
    return this;
}

a.call.call(); //找不到成员
a.call.call(global()); //找不到成员


call方法的第一个参数是它的调用者,如果不传入或传入null则默认是全局对象global(至少JScript是这样的,微软的手册里有写),call方法的真正含义是由用户自己来提供this参数,当call.call()的时候,没有提供call一个this,因此默认的global对象就成为了this参数,然而我们知道call函数是Funciton对象的成员,从一些现象上看很显然call是依赖了this也就是Function的数据结构,那当this变成global对象的时候,自然就异常了。

所以call函数本身不需要对自己的调用者做判断来探查是不是另一个call函数在调用自己,这样做很丑陋,更没有必要
  相关解决方案