最近的面试题几乎都在考 XX 的原理,XX 的源码,XX 的算法,手写代码。原来不懂,只要会用不就行了吗?为什么要考原理、考源码?
这是我原来不太明白的一个问题,后来才发现,懂得源码,懂得原理,写代码就会有一种行云流水般的的流畅感,代码看着也会优雅舒适。
想要做到这一步,需要日积月累。今天也累积一点点。
这个面试题主要是考察变量提升,运算符的优先级。原题是输出的 this
,而不是数字,先看简单一点的,题目如下:
function Foo() {
x = function () {
console.log(1);
};
return this;
}
Foo.x = function () {
console.log(2);
};
Foo.prototype.x = function () {
console.log(3);
};
var x = function () {
console.log(4);
};
function x() {
console.log(5);
}
//请写出以下输出结果:
Foo.x();
x();
Foo().x();
x();
new Foo.x();
new Foo().x();
new new Foo().x();
看这个题之前我们先看一下涉及到的一些知识点。
变量提升
使用 var
声明变量会有变量提升的问题,function
也会提升,看个例子:
// 第一处 a();
var a = function () {
console.log(1);
};
// 第二处 a();
function a() {
console.log(2);
}
// 第三处 a();
上面的代码,在三处调用时,第一处结果是 2
,第二和第三处是1
。
引擎在处理时,会编译成下面这样:
var a;
function a() {
console.log(2);
}
// 第一处 a();
a = function () {
console.log(1);
}
// 第二处 a();
// 第三处 a();
变量的声明会被提升,但是赋值还是在定义的地方处理。函数也会声明,包括函数体,所以会出现上面的结果。
运算符优先级
可以看 MDN 关于 运算符优先级的说明。下图是这里会涉及到的运算符的优先级排列。
运算符优先级.png看了上面的知识,我们现在看一下上面的题目。
Foo.x();
这个很简单,就是访问函数上定义的变量。结果为 2
。
这里的 .
是成员访问,()
是函数调用,都是从左到右执行。
x();
这个就考到了变量提升。
我们看到有三个地方可以产生全局变量 x
。但是在 Foo
函数内定义的,还没有执行,所以这里只有 var x
和 function x
。最后的结果就是 4
。var x
覆盖了 function x
。
Foo().x();
函数调用和成员访问,是同一级别的优先级,从左到右依次执行。Foo()
执行,生成全局变量 x
,这个会覆盖原来的 x
,所以结果是 1
。
x();
这个在上一步的时候已经说了,现在是函数 Foo
中定义的 x
,结果是 1
。
new Foo.x();
这个 new ... 是无参数列表,优先级比成员访问要低。先执行成员访问,可以看成这样 new (Foo.x)()
,然后形成了 new 带参数列表,这个是个整体,执行结果就是 2
。
new Foo().x();
这个有个 new 带参数列表,有个成员访问,有个函数调用,他们是同一级别,执行顺序从左到右,可以写成这样 (new Foo()).x()
最后是调用 Foo
实例上的 x
,最后的结果是就是 3。
new new Foo().x();
这个 有 new 带参数列表,new 无参数列表,成员访问 函数调用。new 无参数列表优先级最低,可以写成这样new (new Foo()).x();
。然后它的执行顺序就可 new Foo.x()
原理一样了,不过这里的是实例上的 x
,所以最后的结果是 3。
这个是比较简单的实例,如果把打印的日志换成 this
,结果又是什么呢?有兴趣可以试一下,自己写出答案,然后运行一下代码,看看和自己想的是否一致。
参考:前端同学经常忽视的一个JavaScript面试题
《你不知道的 JavaScript (上卷)》
网友评论