部分面试题浅析

  几道有意思的js面试题,由于水平所限,欢迎拍砖指正。

控制台输出结果

1
2
console.log('x' in window);   // true
var x = 0;

分析

  in操作符判断属性是否在对象中,并返回一个布尔值。由于变量存在声明提升,因此 var x 会提升到执行环境顶端执行,全局变量会被挂载到 window 对象上,因此 in 操作符返回布尔值true。

分析执行结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
function Foo(){                      // A
getName = function(){
alert('1');
}
return this;
}
Foo.getName = function () { // B
alert('2');
}
Foo.prototype.getName = function(){ // C
alert('3');
}
var getName = function(){ // D
alert('4');
}
function getName(){ // E
alert('5');
}

Foo.getName(); // 2
getName(); // 4
Foo().getName(); // 1
new Foo.getName(); // 2
new Foo().getName(); // 3
new new Foo().getName; // 3

分析

Foo.getName()

  函数是对象,在B处,将一个getName方法当作静态属性挂载到Foo对象中,因此Foo.getName将直接访问这个静态属性,得2。

getName()

  函数声明提升和变量覆盖问题。在这段程序进入执行时,函数声明会率先提升到执行环境的顶端,即E部分首先被执行,getName函数的函数名(一个变量)保存对E函数的引用,随后程序又在 D 处通过函数字面量的方式对getName重新赋予对D函数的引用。因此当程序执行完毕时,getName中保存的是对D函数的引用,因此输出4。

Foo().getName()

  变量声明方式和变量覆盖问题。在 A 处Foo()中,未通过var关键字直接声明了一个函数getName,因此这是一个全局的变量,所以Foo()一经执行,其中声明的getName将覆盖原有的getName变量。在全局环境中执行的Foo函数中return的this仍是window对象,window对象此时再调用getName函数时,取到的是已经被Foo函数中声明getName变量覆盖的引用,因此输出1。

new Foo.getName()

  按我的理解,这里是把Foo.getName当作构造函数执行了。实例化了一个空对象但是里面什么也没有,Foo.getName()执行得到2。

new Foo().getName()

  原型继承。先把Foo函数当作构造函数执行,Foo()中的return this 指向构造出的实例化对象,返回的对象又继承了Foo.prototype中保存的原型方法,因此输出3。

new new Foo().getName

  拆成 new(new Foo().getName)这么来看。new Foo().getName和上面一题 new Foo().getName() 的区别是getName没执行,因此这一步里new Foo().getName创建出了一个带有Foo原型的对象并返回了这个对象从Foo继承而来的的getName方法,对这个函数再进行new操作,和之前类似的,这个对象从Foo继承而来的的getName方法执行了一次,创建出一个空的对象。因此输出3。

0%