这道题涉及到了作用域,this,原型,构造函数,变量声明提升,运算优先级。
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();
1.第一问
Foo.getName()
, 即执行第五行,Foo
的getName
方法。 输出 2.
2.第二问
getName()
直接调用 全局的 getName
函数。那是4还是5呢?第七行与第八行关于getName
的声明有什么不同呢?
- 第七行的
var getName = function(){alert(4)}
是函数表达式 - 第八行的
function getName(){alert(5)}
是函数声明 - 函数表达式 会被拆解为 变量声明 + 变量赋值
- 由于由于变量声明提升的关系,变量声明 和 函数声明 会被提升至最上方(肯定在同作用域的赋值操作之前)。
所以七八行的相对执行顺序改写为如下
var getName; // 原第七行拆解出来的 变量声明
function getName(){alert(5)}; // 原第八行的函数声明
getName = function(){alert(4)}; // 原第七行拆解出来的赋值 (值是函数)
显然,第二问 getName()
的输出是4。
3.第三问
Foo().getName()
字面意思是 Foo()
函数执行完的返回值的getName()
方法的调用。
Foo()
的返回值由第一行可知是this
,因为Foo()
是在全局下调用的所以这里的this
即window
。- 那么
Foo().getName
即全局作用域下的getName
- 由于此刻
Foo()
的执行。Foo()
里面的未声明的getName
的作用域(本作用域里没有)会指向上一层即全局。所以会覆盖之前的
所以 第三问的Foo().getName()
的输出是1。
4.第四问
getName()
,由第三问可知,至此全局(即window
)的getName
应该是在Foo()
函数体内所定义的。
所以第四问的getName()
的输出也是1.
5.第五问
new Foo.getName
显然这里包括第六七问都涉及到了运算符的优先度的问题。是(new Foo).getName
呢?还是new (Foo.getName)
呢。
这里需要先补充一下JS运算符优先级。参照
显然本问的 new 无括号(无参数),比. 优先级低,所以第五问等价于new (Foo.getName)
所以先执行Foo.getName
再创建其实例。输出是2。
第六问
new Foo().getName()
,由上一问的优先级知识可知 new有括号(有参数)与.处于同一优先级,从左往右运算。所以应该等价于(new Foo()).getName
。 显然这里以构造函数Foo()
new 出一个实例对象。由于本身(new Foo())
就是一个空对象,所以需要从Foo
的原型上查找getName
方法。
所以第六问的输出是3。
第七问
new new Foo().getName()
.首先由一元表达式由右向左的规则可知。先计算后面的new Foo().getName
,表达式等价于new (new Foo().getName)()
。 咦?为什么不是new (new Foo().getName())
?
- 这里需要试想一下:
obj.method1()
其实是个执行语句。obj.method
本身是个表达式(类似于函数声明)加上()
之后才是执行它。而()
的优先级最低。
所以第七问的输出也是3