2017.3.27更新
今天在刷题的时候,突然发现之前已经有人在讨论这道题了,而且还涉及到了运算符优先级的问题,这是自己一开始没有想到的。(其实有人也说:程序写多了,自然记住了什么情况下会发生什么样的事情,但是为什么会发生这样的事情,可能问起来一时还真回答不了
。我就属于这种状态,所以一开始并没有考虑到运算符优先级的问题)。
关于该问题的讨论:
今天看到的一道面试题,感觉对理解JavaScript的Function以及原型链和闭包很有帮助。自己并试着讲述一下自己的理解,欢迎拍砖。
题目:
function Foo() {
getName = function() {
alert(1);
}
return this;
}
Foo.getName = function() {
alert(2);
}
Foo.prototype.getName = function() {
alert(3);
}
var getName = function() {
alert(4);
};
function getName() {
alert(5);
}
//调用部分
Foo.getName();
getName();
Foo().getName();
getName();
new Foo.getName();
new Foo().getName();
new new Foo().getName();
请问上述代码的输出结果是什么?
2,4,1,1,2,3,3
分析
Foo.getName()
:函数调用输出2很好理解,直接调用的Foo.getName()
;-
getName()
:函数调用输出为4,这和变量提升有关系了,因为在函数调用分为两个步骤,第一进入上下文阶段,第二为执行阶段。进入上下文时,会获取arguments
,函数声明
,变量声明
。只有在执行阶段才会进行变量赋值,而第四个是函数表达式,第五个为函数声明,所以他们等同于下面的形式:进入上下文阶段:
function getName(){alert(5);}
执行阶段(将之前的getName函数给覆盖掉了):
getName=function(){alert(4);}
所以不管怎么调用,答案中都应该不会出现5。
-
Foo().getName()
与第二次getName()
:第三个函数调用开始有迷惑性了。最后调用的getName
函数,其实是全局的getName
。第二次调用就成了Foo()
函数中的那个,因为其前面没有var
,也就是说这个getName
并不是一个私有变量,而是全局变量,所以将之前的全局中的getName
函数在执行Foo()
时会被覆盖掉了。因此下一次再执行getName
方法的结果就变成了1
,而不是之前的4
了。另外也跟
new
关键字有关,因为Foo()
前没有使用new
,所以不会创建新的对象,而且Foo
的调用应该属于函数调用,所以返回的this
其实是window
对象,而不是Foo
实 例(并没有创建)。 new Foo.getName()
:调用其实是创建了一个Foo.getName
的新的实例(函数本身也是Object),在创建对象的过程中执行到了alert(2)
语句,所以就输出为2;new Foo().getName()
:调用是先根据Foo.prototyoe
创建一个Foo
的实例,调用getName
方法时,因为自身没有getName
方法,会去原型链上找,最后调用到Foo.prototype.getName
,所以就是输出为3;new new Foo().getName()
:第七个就是第五和第六个的结合,先创建一个Foo
实例,然后再创建Foo
实例的getName
函数(也就是Foo.prototype.getName
)的实例。在创建的过程中,执行到alert(3)
语句,所以输出3。
变化
通过修改Foo()函数体,可以呈现出不同的调用变化。
Foo函数体内的
getName
前加上var
(getName
变成了私有变量),答案会变成:2,4,4,4,2,3,3
。Foo函数体内的
getName
前加上this
(getName
变成了属性),答案会变成:2,4,1,1,2,1,1
。