一道面试题的解析

版权声明:本文为博主原创文章,转载请附上原文地址 https://blog.csdn.net/writing_happy/article/details/82217344

原题

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()  //???

  答案你可以轻易得到,因为有控制台输出。

解析

  首先你需要明白的是,写了这么多,现在一共有几个变量。变量提升我相信大家应该都清楚的吧,在这里我们可以把上边的代码分成声明部分和赋值部分如下:

//声明部分
var getName
function Foo(){
  getName = function(){ alert(1) }
  return this
}
function getName(){ alert(5) }
//赋值部分
Foo.getName = function(){ alert(2) }
Foo.prototype.getName = function(){ alert(3) }
getName = function(){ alert(4) }

  这段代码最终得到的是什么呢?
  一个全局变量getName,一个函数Foo。其中这个函数里边有一个变量getName,变量的值是一个匿名函数function(){ alert(2) },除此之外这个函数的原型链上也有一个变量getName,它的值是一个匿名函数function(){ alert(3) }。这里需要注意的是函数Foo声明的那段代码其实并没有在函数内部创建一个getName变量,这里做的实际上是把全局的getName重新赋值。
  或者如果你没有看明白大可以给它换一个变量名,比如下边的:

var getA
function Foo(){
  getA = function(){ alert(1) }
  return this
}
function getA(){ alert(5) }
//赋值部分
Foo.getB = function(){ alert(2) }
Foo.prototype.getC = function(){ alert(3) }
getA = function(){ alert(4) }

  在这一步你需要读懂的是,你在修改的到底是哪个变量。

答案

  在真正分析答案的时候你需要知道的是运算符的优先级。
1. Foo.getName()
  此时调用的是Foo内部的getName函数,也就是我说的getB,由上边的代码你可以轻易看出结果是alert(2)
2. getName()
  调用的是全局的getName,也就是getA,最终值是alert(4)
3. Foo().getName()
  此句可以拆分为

var a = Foo()  
a.getName()

  第一句执行了全局变量getName的赋值(getA),所以此时getName = function(){ alert(1) }。同时函数Foo返回的是this,此时this指的是全局对象window。
  第二句实际上是window.getName(),我们知道,此时调用的是全局的getName,也就是getA。因此结果是alert(1)
4. getName()
  调用的是全局的getName,所以结果也是alert(1)
5. new Foo.getName()
  拆分为

var a = Foo.getName()
new a 

  其中第一句执行了Foo.getName()这个函数,此时已经有了运行结果alert(2),第二句的new a实际上只是使用这个函数构造了一个对象,并不产生实际的运行效果。如果你在Foo.getName这个函数的prototype上挂一个变量getD,比如

Foo.getName.prototype.getD = function(){ alert(6) }

  那么你运行new Foo.getName().getD()得到的会是alert(2)alert(6)
6. new Foo().getName()
  拆分为

var a = new Foo()
a.getName()

  这个的疑问可能会集中在先运算哪一个的上边,你可以查一查运算符的优先级。new Foo().getName()的优先级是相同的,所以按顺序来。在上一题中,new Foo.getName()优先级低,所以先运行.getName()
  new Foo()得到的是Foo构造出来的对象,Foo新构造出来的对象并没有getName方法,所以在后边调用getName方法的时候会向它的原型链上查找,所以此时调用的是原型链上的getName函数,所以结果是alert(3)
7. new new Foo().getName()
  拆分为

var a = new Foo()
var b = a.getName()
new b

  依旧是运算符的优先级问题,new aa.getName()的优先级低,所以先执行的是a.getName(),此时已经得到了运行结果,第三句的new b只是构造了一个函数,并没有产生实际的效果。所以这个的结果也是alert(3)

猜你喜欢

转载自blog.csdn.net/writing_happy/article/details/82217344