经典面试题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
至此这道题就分析完成了