liangbus / blogging

Blog to go
10 stars 0 forks source link

一道题很“基础”的前端笔试题 #30

Open liangbus opened 4 years ago

liangbus commented 4 years ago

昨天在某公众号上看到下面这么一道题,绕来绕去,但是如果基础不够扎实,也很容易掉坑里,像我刚拿到手写了一下答案,再跑一下,发现自己还是有点知识点有所遗漏,所以开一篇来逐步分析,以加强理解

题目

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();

这一题咋一看,说实话,单词就这么几个,但考察的知识点却很多:全局属性,变量提升,原型属性,继承,函数声明等等

Foo.getName();

结果 alert(2)

Foo 是一个函数,因为后续有给函数声明了一个静态属性 Foo.getName,所以其执行结果就是 alert(2),这里要注意的是,正常情况下函数外部是无法访问到函数内部的属性和方法(这属性私有属性和方法),所以这里不会是 alert(1). 另外有些人可能会被 Foo.prototype.getName 这里给误导了,纠结其会不会覆盖上面的属性,实际上这里声明的是公共方法/属性,通过 Foo.getName 是无法访问到的,除非声明在 proto 上面,实际结果可以自己测试一下。

getName();

结果 alert(4) 这里直接调用 getName 方法,前面没有对象,因些是全局调用,因此我们只需要关心声明在 window 下的 getName 方法,如下

var getName = function () { alert (4);};
function getName() { alert (5);}

这里考察的是 var 关键字的声明提升和函数声明的优先级

函数会首先被提升,然后才是变量 —— 《你不知道的 JavaScript 上卷》

所以这里经过浏览器的预处理,变成

function getName() { alert (5);}
var getName = function () { alert (4);};

alert(5) 会被提升到最高,然后才到 var 关键字声明,再赋值,所以 alert(4) 会覆盖 alert(5)

拓展

console.log(foo)
function foo() {
    return 333
}
var foo = 123

foo // ???

这里可以看到,如果函数更高优先级,那么 var 再声明的话, foo 是不是应该是 undefined ?

事实并不是

foo
ƒ foo() {
    return 333
}

函数提升优先级比变量提升要高,且不会被变量声明覆盖,但是会被变量赋值覆盖。

Foo().getName();

结果

alert(1)

说实话,这里我一开始也是做错了,我认为是 alert(4),当时没发现,Foo 函数里面的 getName 是不带 var 关键字的,所以是相当于覆盖全局的 getName,return this 这里的 this 指的是全局 window。(更多关于this 的介绍,可以查看《你不知道的 JavaScript 上卷》,里面用了两个章节去说明,很详细)

getName();

这里跟之前的一个调用一样,但是因为上面一个执行,导致全局的 getName 被覆盖了,所以输出跟上面一样

new Foo.getName();

结果

alert(2)

以 Foo.getName 作为构造函数实例化,前面我们知道 Foo.getName 是什么,所以 new 就直接把它执行一遍了

new Foo().getName();

结果

alert(3)

这里比上面的多了一个括号,如果不知道 new 运算符的优先级的话,这里很可能就答不对,实际上是 (new Foo()).getName() 这样的运算过程,具体可以看这篇 new与属性访问的顺序 这可以看出,先实例化 Foo,然后取其原型上的 getName 方法

new new Foo().getName();

结果

alert(3)

其实觉得这题有点BT,为了考而考吧 = =|| ,直接抄答案吧,还是考察优先级顺序的问题 new ((new Foo()).getName)();