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);};
// please output the results:
foo.getName();
getName();
foo().getName();
getName();
new foo.getName();
new foo().getName();
new new foo().getName();
JS半吊子又杀回江湖了,这次看到网上一道很火的JS面试题,看着看着就跪了。果然纯逻辑和考核心概念的题目很容易出错,现在就来分析分析。
题目:
眩晕了·····
要搞懂这题,主要是以下几个概念要明白,函数,静态属性,原型。
首先,定义名为foo的函数。再为foo创建getName的静态属性,这个属性存储了匿名函数。接着为foo的原型对象创建getName的匿名函数。再创建一个名为getName的函数。最后声明一个叫getName的函数。
第1问
第2问
这题太坑,最重要的一点要明白变量声明提升和函数表达式。变量声明提升就是说,所有声明变量和声明函数都会提升到当前函数的顶部。函数表达式是指
var
的方式来声明,这时js会将代码拆分为两行来执行,声明的会提前到顶部,赋值会在原位置。所以这题的执行顺序就变成:所以这里返回的是4.
第3问
想的很简单,虽然答案对了,但知其然不知其所以然呀!过程是这样的:问
foo().getName()
,就是先执行foo函数再调用其返回值对象的getName属性函数。首先foo()
是一个声明的函数,函数的第一句并没有声明getName属性,而是直接赋值了一个函数,这就要向当前函数作用域上层(即外层作用域)找getName变量,外部有声明(如果一直向上找到window对象都没有此属性,就会为window对象创建这个属性),就将此变量的值赋值为一个函数。注意,外层作用域声明的getName值就变味被赋值的函数!这就非常有意思了,getName值在整个过程中变了3次!最后foo()返回this,这里的this指向window对象。所以此问返回的是1.
第4问
上问执行的相当于是
this.getName()
,其实就是本题的答案,还是返回1.第5问
原来还有优先级的不同,成员访问要高于new的优先级,所以相当于
new (foo.getName)();
,返回2.MDN关于优先级的文档:运算符优先级
第6问
看完优先级后,懵逼的我…这里是运算符高于new高于函数调用,故执行:
(new foo()).getName()
这样原函数foo执行后返回this,this代表当前实例化对象,也就是foo函数返回实例化对象,再调用其实例化对象的getName函数,在构造函数中,并没有为实例化对象添加属性,所以从当前对象的原型对象(prototype)中寻找。这里有点绕,我在console下跑了后仔细看看发现是这么回事,
new foo()
后返回的实例化对象里面并没有构造函数里的getName属性函数,添加了prototype才找得到。此处一个概念叫构造函数的返回值,就是指构造函数的实例化对象。分为以下3种情况:
本身函数就没有返回值,那new之后就返回实例化对象。
函数有返回值,但是是非引用类型,如基本类型,就跟1的情况相同。
返回值是引用类型,实际返回值就是那个引用类型。
此处返回3.
第7问
还是优先级问题,这里执行顺序为:
new ((new foo()).getName()
,先里面的foo函数,再new一个实例化对象,后面是进行成员访问.getName,最后再new。好复杂...已经晕了。其实这里没有括号,所以先成员访问,左侧取值时要注意。总之:括号>成员访问>new