Function.prototype.call 内部实现探讨
今天在网上看到了一个有意思的js题目,就拿去和同事讨论。本来以为是一个很简单的问题,但越讨论越深入,逐步认识到了这个问题的深度。 题目是这样的:
Java代码
{
alert(1);
}
function f2()
{
alert(2);
}
var f3 = f1.call;
f3.call(f2);
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做了不同的实现,伪码如下:
Java代码
{
this(b,c);//忽略this内部实现与a的绑定;
}
call.call = function(a,b,c)
{
a(b,c);//忽略this的绑定
}
Function.prototype.call = 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的实现做了一个统一的托管,根据调用对象的不同,实现不同的逻辑。伪码如下:
Java代码
{
if(this === call)
{
a(b,c);
}
else
{
this(b,c);
}
}
Function.prototype.call = call;
|