废话少说,上题目。
function Foo(){
getName=function(){console.log(1)}
return this
}//1
Foo.getName=function(){console.log(2)}//2
Foo.prototype.getName=function(){console.log(3)}//3
var getName=function(){console.log(4)}//4
function getName(){console.log(5)}//5
getName() // ?
Foo().getName()// ?
getName() // ?
Foo.getName() // ?
new Foo.getName() // ?
new Foo().getName()// ?
new new Foo().getName() // ?
请思考一下,依次输出什么?
1、变量提升
首先,本题考察了变量提升和函数提升。函数提升在变量提升之前,所以编译以后的代码如下。
function Foo(){
getName=function(){console.log(1)}
return this
}//1
function getName(){console.log(5)}// 函数提升
var getName;// 变量提升
Foo.getName=function(){console.log(2)}//2
Foo.prototype.getName=function(){console.log(3)}//3
getName=function(){console.log(4)}//4
getName() // ?
Foo().getName()// ?
getName() // ?
Foo.getName() // ?
new Foo.getName() // ?
new Foo().getName()// ?
new new Foo().getName() // ?
值得注意的是,在var getName之后,getName = function(){console.log(4)}之前,此时getName并不为undefined,此时getName()会输出 5,在getName=function(){console.log(4)}之后,getName被重新定义,此时再调用getName()会输出4。
所以第一个输出4
2、this指向
我们再来分析Foo().getName(),这里其实有一个大写陷阱,Foo()是一个普通函数,而不是一个构造函数。调用Foo()函数,执行getName=function(){console.log(1)},重写getName变量,返回this。其实this指向也很简单,函数在哪里被调用,this就指向谁,当前调用Foo()的this很显然指向window。
第2行代码实际上做了两个操作,重写getName,再调用getName,所以输出1
第2行代码分析清楚以后,第3行代码也就迎刃而解了,getName被重写了,第三行仍然输出1
3、原型链和new操作符
第4行,Foo.getName(),很显然,Foo.getName=function(){console.log(2)},所以输出2
第5行,new Foo.getName(),这里就要注意了,new操作符一定要找到一个构造函数,如果找不到就会报错。而在js中,你用new修饰时,普通函数就是构造函数,不用new修饰时,它就是普通函数。语法: new 函数名(),可以看到new Foo并不符合new操作符的语法,把Foo.getName看成一个函数名,这样就符合new的语法了。调用构造函数,输出2。
第6行,new Foo()符合new操作符语法,返回一个Foo对象f,调用f.getName(),f对象上没有getName属性,会检索f__proto__.getName(),即在f的构造函数的原型对象(Foo.prototype)上检索,所以f.getName()调用的是Foo.prototype.getName(),输出3
原型链有点绕,简单来说就是一个对象的原型指向其构造函数的原型对象,f.__proto__ === Foo.prototype,当对象找不到属性就会在其原型上找,一直检索到Object.prototype,如果Object.prototype上也没有,再想其原型Object.prototype.__proto__上找,而Object.prototype.__proto__指向null,所以Object.prototype上没有检索到就会返回undefined。这一块不太清楚的同学可以站内搜索原型链
第7行,两个new操作符,可能一下就懵了,但是搞清楚new操作符匹配的规则以后,就很好解决了。
首先new Foo()符合new语法,调用构造函数,什么都没输出,返回Foo的实例对象f,new f.getName()符合语法,f.getName 指向Foo.prototype.getName,调用构造函数,输出3
最后输出:
4
1
1
2
2
3
3
你都答对了吗?