考题:以下表达式的结果是什么?
function Foo(){
getName = function () {
console.log(1)
};
return this
}
Foo.getName = () => console.log(2);
Foo.prototype.getName = () => console.log(3);
var getName = () => console.log(4);
function getName() {
console.log(5)
}
// 以下表达式的结果是什么?
Foo.getName(); // 2
getName(); // 4
Foo().getName(); //1
getName(); //1
new Foo.getName(); // 报错
new Foo().getName(); // 3
new new Foo().getName(); // 报错
分析:
这道题基于 JS 执行顺序来做。
JS 执行顺序是:
预解析阶段:检查代码错误,变量提升,提前声明函数
执行阶段:从上到下执行代码,完成变量赋值等
因此,上面代码执行过程如下:
1~
function Foo(){
getName = function () {
console.log(1)
};
return this
}
声明构造函数 Foo
以及该构造函数内的全局变量 getName
解析:
- 构造函数
Foo
内的变量getName
, 并不是在构造函数内定义的,所以属于全局变量,会被提前声明,只有在Foo
作为函数被调用时,才会被赋值。 - 构造函数
Foo
返回this
,Foo
作为函数被调用时,返回调用者
2~
var getName
声明变量 getName
3~
function getName() {
console.log(5)
}
声明函数 getName
解析:
此时函数名 getName
和前面声明的变量名getName
重复 ,函数会覆盖变量,因此 getName
此刻是 打印 5 的函数
4~
Foo.getName = () => console.log(2);
给函数 Foo
添加私有方法 getName
解析:
函数名Foo
加方法名getName
可以直接调用这个函数
5~
Foo.prototype.getName = () => console.log(3)
在构造函数 Foo
的原型上添加 getName
的方法,Foo
的子类可以调用
6~
getName = () => console.log(4)
给变量 getName
赋值
解析:
会覆盖之前声明为 getName
的函数,getName
此刻是 打印4的函数
前面几步完成了准备,以下是题目考察的语句:
7~
Foo.getName() // 2
解析:
这里直接调用 Foo
的私有方法 getName
函数,从上面 第4 看,这里打印 2
8~
getName() // 4
解析:
这是全局 window 调用 getNam
e, 此时 全局的getName
从第6可以看出,是 打印 4 的函数,因此这里打印 4
9~
Foo().getName() // 1
解析:
这里Foo()
先调用了Foo
函数,有两个结果:
一是返回 this, 上面的表达式变成了 this.getName()
, 此时 this 指向全局 window;
二是给 Foo
里面的变量 getName
赋值了一个打印 1 的函数, 此时全局中的 getName
变成 打印 1 的函数。
因此这个表达式最终打印 1
10~
getName() // 1
解析:
这里是全局window 调用getName
, 由于上一行代码已经将 全局中的 getName
变成 打印 1 的函数,因此这里结果 打印 1
11~
new Foo.getName() // 报错
解析:
这里使用了 new 运算符,new 后面需要接构造函数。
上面语句断句:new (Foo.getName())
Foo.getName()
是调用Foo
的私有方法,结果是 2,不是构造函数,这里会报错
12~
new Foo().getName() // 不考虑上一句报错的情况下,结果是 3
解析:
这条语句的断句: (new Foo()).getName()
new Foo()
是创建了一个Foo
的实例对象,然后该实例对象调用方法 getName()
Foo
的实例对象本身没有 getName
方法,但是它的构造函数Foo
的原型 prototype 上有该方法,Foo
的实例对象通过原型链调用这个方法。
Foo
原型上的 getName
方法打印 3,因此不考虑上一条报错的话,这条语句打印3
13~
new new Foo().getName() // 报错
这条语句的断句:new ((new Foo()).getName())
((new Foo()).getName()
从上一条语句已知结果是3,不是构造函数,不能用 new 运算符,因此这条语句也报错