new new Foo().getName() 一道前端面试题引发的思考
今天想着邻近毕业季,估计又会有很多稀奇古怪的面试题,看掘金社区的时候看到一道贼有意思的题,一开始看的十分懵逼,后来捋了捋,将心得总结一下。
function Foo() { getName = function () { console.log(1); } return this; } Foo.getName = function () { console.log(2); } Foo.prototype.getName = function () { console.log(3); } var getName = function () { console.log(4); } function getName() { console.log(5); } Foo.getName(); getName(); Foo().getName(); getName(); new Foo.getName(); new Foo().getName(); new new Foo().getName();
ok,下面我来一点一点分析一下这段代码到底要做个啥。首先第一个
Foo.getName(); //2
第一个是2这肯定是没问题的。
我们继续往下
getName(); //4
第二个为4的原因为啥不是5呢,这就涉及到变量提升的问题了,因为函数声明会提升到执行环境的顶部,而函数表达式只有变量var getName会提升看起来就像是这样
function getName(){ console.log(5); } var getName; getName = function(){ console.log(4); }
所以函数声明getName会被盖掉,所以这里是5;
ok,继续往下看
Foo().getName(); //1
这。。。。其实仔细看的话也是可以看出来的,我们在执行Foo()函数的时候getName这个变量提升到外部的全局作用域中了,因为在js中,如果对于一个变量没用用var 或者 let等声明的话,他就默认是全局属性,就是window对象的一个属性。所以在这里我们的全局的getName又被改了
因为我们Foo()执行的时候返回了this而这里的this就是window对象,关于为什么是window这里不再展开讲,不然就写不完了。。。。
我们需要知道的是在浏览器中所有全局的声明都是window对象的属性和方法,所以这里我们调用this.getName()就会返回1了。
这里需要注意的是如果你是在node环境下打印这些值的话,这里是会报错的,因为在node环境下全局对象为global对象,而它区别window对象一个重要区别是,声明的函数,变量不是global的属性,所以你就找不到getName这个方法;而且会影响下一个的取值,下一个就会是4了。
接下来我们再一次执行
getName(); // 1
因为已经被修改了,所以不难看出来。接下来
new Foo.getName(); //2
在这里我们通过new 来返回一个 Foo.getName 的实例。因为new的过程中一步很重要的操作是通过call将this绑定到实例对象上,并将实例对象返回,ok,既然有call的过程那么就很容易明白他这里输出了2;
我们可以验证一下结果
var bar = new Foo.getName(); bar.__proto__.constructor === Foo.getName //true
这样就能更直观的看出,我们这样通过原型链看出他是Foo.getName的实例;
接下来
new Foo().getName(); //3
在这里我们通过new Foo()操作返回一个实例对象,这样我们就可以通过原型链__proto__找到getName这个方法并执行他。
最后
new new Foo().getName(); //3
这就是最让人懵逼的地方,他到底在干啥呢,其实他和倒数第三个很像,他其实是对Foo().getName这个函数进行了实例化,具体可以看倒数第三个,所以这里会输出3
而且我们可以在控制台上打印一下
var bar = new new Foo().getName();
打印这个bar看下结果吧。
这道题大致就是这样,如果本人有写错的地方,还望指出,毕竟功力尚浅,欢迎留言进行交流。