经典面试题Foo与getName() (转载)

从一道面试题出发

按照惯例,还是从面试题出发。用一道面试题引出相关的知识(日常面向面试学习 😃 )

 1 function Foo(){
 2   getName = function(){
 3     console.log(1);
 4   }
 5   return this;
 6 }
 7 Foo.getName = function(){console.log(2);}
 8 Foo.prototype.getName = function(){console.log(3);}
 9 var getName = function(){console.log(4);}
10 function getName(){console.log(5);}
11 
12 Foo.getName();
13 getName();
14 Foo().getName();
15 getName();
16 new Foo.getName();
17 new Foo().getName();
18 new new Foo().getName();

据闻是一道字节的面试题,里面涉及的知识点很多很杂,是一道综合性很强的题。

# 1. 代码细节

这里先标注一些细节点。

 1 function Foo(){
 2   //注意这里getName前面没有var关键字,定义的是一个全局变量。
 3   getName = function(){
 4     console.log(1);
 5   }
 6   //这里是this的默认绑定,返回值为window
 7   return this;
 8 }
 9 //给Foo定义getName方法,注意区分和Foo函数里面的那一段代码,意义并不相同。
10 Foo.getName = function(){console.log(2);}
11 //给Foo的原型添加getName方法
12 Foo.prototype.getName = function(){console.log(3);}
13 //定义全局getName方法,这里注意变量提升
14 var getName = function(){console.log(4);}
15 function getName(){console.log(5);}
16 
17 Foo.getName();
18 getName();
19 Foo().getName();
20 getName();
21 //这里的三个涉及到运算符优先级问题
22 new Foo.getName();   //new (Foo.getName());
23 new Foo().getName();   //(new Foo()).getName();
24 new new Foo().getName();   //new ((new Foo()).getName());

知识补充:

1.声明提升
JavaScript 中,函数及变量的声明都将被提升到函数的最顶部。即可以先使用后声明。这里一定注意只时进行了声明的提升,初始化过程并没有跟随提升
如:var a=10,只将var a进行了提升,提升到当前作用域的顶部

2.原型
对原型不了解的同学可以看这篇文章:https://blog.csdn.net/qq_40294512/article/details/116563351

3.函数调用方式

test() 直接调用
obj.test() 通过对象调用
new test() new调用
test.call/apply(obj) 临时让test成为obj的方法进行调用
4.js运算符优先级

 

 

然后大家可以自己过一遍代码推算一下结果,对比一下和标准答案是否一样。

答案:2 4 1 1 2 3 3

# 2. 代码分析

 1 function Foo(){
 2   getName = function(){
 3     console.log(1);
 4   }
 5   return this;
 6 }
 7 Foo.getName = function(){console.log(2);}
 8 Foo.prototype.getName = function(){console.log(3);}
 9 var getName = function(){console.log(4);}
10 function getName(){console.log(5);}
11 
12 Foo.getName();
13 getName();
14 Foo().getName();
15 getName();
16 new Foo.getName();
17 new Foo().getName();
18 new new Foo().getName();

然后我们一行一行分析代码,首先先做一个变量提升(这里只是为了方便理解)

 1 function Foo(){
 2   getName = function(){
 3     console.log(1);
 4   }
 5   return this;
 6 }
 7 var getName;
 8 function getName(){console.log(5);}
 9 Foo.getName = function(){console.log(2);}
10 Foo.prototype.getName = function(){console.log(3);}
11 getName = function(){console.log(4);}
12 
13 Foo.getName();
14 getName();
15 Foo().getName();
16 getName();
17 new Foo.getName();
18 new Foo().getName();
19 new new Foo().getName();

1.Foo.getName() 调用Foo里的getName方法。没有什么好说的,直接输出2
2.getName() 这里getName被赋值了两次,取最后一次赋值,输出4
3.Foo().getName() 首先执行Foo(),getName被再次赋值,然后返回this(即window),然后执行this.getName() ,输出1
4.再次调用getName() 此时全局的getName() 已经被修改为Foo内的那个函数,输出1
5.new Foo.getName() 这里是使用new方法调用Foo.getName(),输出2
6.new Foo().getName() 先new一个Foo对象,然后调用Foo的实例对象的getName()方法,实例对象中并不存在getName方法,于是返回原型链查找,调用原型链上的方法,输出3
7.new new Foo().getName() 先new一个Foo对象,然后用new的方式调用原型链上的方法,输出3

至此这道题就分析完成了

posted @ 2021-07-31 11:07  一个动态类型的幽灵  阅读(140)  评论(1编辑  收藏  举报