Codog

关注微信公众号:Codog代码狗

0%

变量提升与函数声明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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();

尝试说出输出结果?

结果: 2 4 1 1 2 3 3

这道题考察了全局对象、函数声明提升、变量提升、this指针、原型、构造函数、new运算符与点运算符的优先级。

个人理解:

加载代码

var存在变量提升,即会把声明提到第一行,而定义位置不变,函数声明同样,执行代码之前会先读取函数声明,所以相当于:

1
2
3
4
var getName
function getName(){ console.log(5) }
// ....
getName = function(){ console.log(4) }

函数的优先级高于变量,相同名称的函数,后面的会覆盖前面的,所以代码加载解析后定义在全局上的getName方法输出为4

执行代码

  • 第一行 Foo.getName()

函数也可以理解为特殊的对象,所以可以为它添加属性,输出2;

  • 第二行 getName()

执行全局上的getName方法,输出4

  • 第三行 Foo().getName()

指定Foo方法,方法返回this,此时this指向window对象,但之前又对getName重新赋值,所以全局getName方法改为输出1

  • 第四行 getName()

全局方法getName,上面已经将输出改为1,所以输出1

  • 第五行 new Foo.getName()

考察运算符优先级问题,是以Foo.getName当作构造方法还是Foo.getName()返回结果当作构造方法。

image

上图显示无参数new方法的优先级(18)低于取值运算(19),所以之际执行是new (Foo.getName)(),即会以Foo.getName当作构造方法,输出2

  • 第六行 new Foo().getName()

有参数new方法的优先级与取值运算相同(都是19),所以从左向右执行,(new Foo()).getName(),Foo作为构造方法返回实例,再对实例取getName方法,实例取值在原型链上获取,所以执行Foo.prototype.getName方法,输出3

比如经常写的new Set(…arr).size也是这样的运算规则

  • 第七行 new new Foo().getName()

多个一元运算符存在时,执行顺序为从右到左,所以执行方式为new (new Foo().getName)(),而new Foo().getName的执行结果与第六行相同,返回Foo原型上的getName方法,以该方法作为构造方法生成实例,输出为3

参考文章:https://juejin.im/post/5dfc2739e51d45584006e644