求下列代码的输出。
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();
我们来一一分析下吧。首先我们要明白,通过var声明的变量和函数的声明都会被抬升,在代码正式执行之前就会被解析。所以我们先找到函数声明和通过var声明的变量。得到如下结果
Foo = function () {
getName = function () { alert (1); };
return this;
}
getName = undefined
getName = function () { alert (5);}
然后从头到尾开始执行代码。
第一问
Foo.getName();
上面这行代码,访问的是Foo函数的静态属性,显然答案是2.
第二问
getName();
这里刚开始会把function (){alert(5)}赋给getName,但由于后来代码执行过程中把function (){alert(4)}赋给了getName,所以这里的输出结果为4.
第三问
Foo().getName();
这里先执行Foo函数,由于是在全局环境调用的Foo,所以它返回的this指向window,相当于是求window.getName(),这里要注意,在执行Foo()时,在函数体内执行了getName = function () { alert (1); 而由于这里getName并没有被声明,所以它会返回到上一级去找getName,刚好全局环境中有定义这个变量,所以就把function () { alert (1)赋给了全局变量getName,所以当执行Foo().getName()时返回1.
第四行
getName();
根据上面的分析,这里就显然返回结果1.
第五问
new Foo.getName();
这里考察JS运算符的优先级。 我也是查了资料才知道,带参数的new和成员访问 ….… 的优先级相同,高于不带参数的new和函数调用()。因此第五问就转化为
new (Foo.getName)( ),此时因为有个括号,所以变成了new有参数列表,直接执行new,再函数调用,所以这里是将getName作为构造函数来执行,所以弹出2.
第六问
new Foo().getName();
这里有参列表和 . 的执行顺序是一样的,所以按顺序执行就可。注意这里Foo作为构造函数会有返回值,而返回值this在构造函数中本来就代表当前实例化对象,最终Foo函数返回实例化对象。 之后调用实例化对象的getName函数,因为在Foo构造函数中没有为实例化对象添加任何属性,当前对象的原型对象(prototype)中寻找getName函数。所以弹出3.
第七问
new new Foo().getName();
最终执行顺序为new ((new Foo()).getName)(),先初始化Foo的实例化对象,然后将其原型上的getName函数作为构造函数再次new,所以最终结果为3。