水群的时候看到 这样一道题
<script>
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();
</script>
分析:
Foo.getName()
访问Foo函数上存储的,一个叫做getName的静态属性,该属性保存的值是匿名函数,这里输出2。
getName()
直接调用那么就是访问当前上文作用域内的叫getName的函数,但是此处有两个坑,一是变量声明提升,二是函数表达式。
变量声明提升
函数声明提升指所有声明变量或者是声明函数都会被提升到当前函数的顶部。
// 样例
console.log('x' in window);//true
var x;
x = 0;
// 代码执行时js引擎会将声明语句提升至代码最上方,变为:
var x;
console.log('x' in window);//true
x = 0;
函数表达式
var getName 与 function getName 都是声明语句,区别在于 var getName 是函数表达式,而 function getName 是函数声明。
// 样例
console.log(x); //输出:function x(){}
var x=1;
function x(){}
// 实际执行的代码为,先将 var x=1 拆分为 var x; 和 x = 1; 两行,再将 var x; 和 function x(){} // 两行提升至最上方变成
var x;
function x(){}
console.log(x);
x=1;
// 所以最终函数声明的x覆盖了变量声明的x,log输出为x函数
// 举一反三
var x=1;
function x(){}
console.log(x); //输出:1
再来看这道题
function Foo() {
getName = function () { alert (1); };
return this;
}
var getName;//只提升变量声明
function getName() { alert (5);}//提升函数声明,覆盖var的声明
Foo.getName = function () { alert (2);};
Foo.prototype.getName = function () { alert (3);};
getName = function () { alert (4);};//最终的赋值再次覆盖function getName声明
getName();//最终输出4
Foo().getName()
Foo函数的第一句 getName = function () { alert (1); }; 是一句函数赋值语句,注意它没有var声明,所以先向当前Foo函数作用域内寻找getName变量,没有。再向当前函数作用域上层,即外层作用域内寻找是否含有getName变量,找到了,也就是第二种情况中的alert(4)。在找到后将外层作用域内的getName进行修改。此处若依然没有找到会一直向上查找到window对象,若window对象中也没有getName属性,就在window对象中创建一个getName变量。在当前情况下全局window中的getName已经被修改成了alert(1)。
之后Foo函数的返回值是this,简单的讲,this的指向是由所在函数的调用方式决定的。而此处的直接调用方式,this指向window对象,相当于执行 window.getName(),而window中的getName已经被修改为alert(1),所以最终会输出1。
getName()
直接调用getName函数,相当于 window.getName() ,因为这个变量已经被Foo函数执行时修改了 ,遂结果与上一问相同,为1。
new Foo.getName()
涉及到了优先级的问题,这句话相当于是 new (Foo.getName)(),结果为2。
new Foo().getName()
这句话相当于是 (new Foo()).getName(),也许会好奇为什么不是new (Foo().getName()),new Foo() 可以理解为两种运算:new 带参数(即 new Foo())和函数调用(即 先 Foo() 取值之后再 new),而 new 带参数的优先级是高于函数调用的,因此先执行了 new Foo(),获得 Foo 类的实例对象,再进行了成员访问 .getName。
new new Foo().getName()
这句话相当于是new ((new Foo()).getName)()