变量申明的提升,闭包,作用域,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(); 

整个题目涉及到很多知识:首先我们要理解执行上下文:http://blog.163.com/no_404/blog/static/2412460752014102683438277/

我们可以知道在第一问之前js已经做了哪些事:   

function Foo(){};
var getName;//变量申明的提升:即所有申明变量或者申明函数都会被提升到当前函数的顶部
/*
例如:console.log('x' in window);//true
         var x; x = 0; 
此代码执行的时候js引擎会将申明语句提升至代码最上方变为:
var x;
console.log('x' in window);//true
x=0;

函数表达式的申明也会提升:
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; 

*/
function getName(){};
Foo.prototype.getName;
Foo.getName = function () {};
Foo.prototype.getName = function () {};
getName = function () {};

执行第一问Foo.getName();的时候相当于激活了Foo.getName;也就是:

Foo.getName = function () { alert (2);};//所以答案是2

执行第二问的时候getName();相当于激活对全局getName函数的调用,因为有两次对getName方法的书写,所以后面一次会覆盖第一次的方法,也就是对函数变量getName赋值的方法会覆盖掉直接申明函数getName时候对方法的实现,即执行getName=function(){alert(4);}所以答案是4;

执行第三问Foo().getName();先执行了Foo()函数function Foo(){getName=function(){alert(1);};return this;},这个函数执行时返回一个this,this此时为当前对象Foo函数,然后再调用返回值对象的getName函数,此时激活Foo()的变量函数getName=function(){alert(1);}也就是Foo函数的属性:getName函数,所以此时返回1;

执行第四问的时候因为第三问激活并执行了Foo().getName();此处foo()函数里面的getName函数因为没有加var命令,所以里面的getName()函数是一个全局变量,所以第三问执行之后,之前第二问时候全局变量为alert(4)的getName()被重写了,所以此时getName()函数运行结果为1;

第五问,new Foo.getName();此处需要知道js运算符优先级:

我们先看看运算符分优先级:

可以知道.的优先级高于new,圆括号优先级高于.;所以此式子正确打开方式是:new (Foo.( getName() ));注意此时的Foo没有加括号 也就是说只是调用它的属性,前面加上了new 相当于将Foo的成员getName函数作为了构造函数执行,所以答案是2;

第六问:正确打开方式:new (Foo().getName()) ;new Foo() 返回this,this在构造函数中代表一个当前实例化对象,所以最终返回的是实例化对象,再调用实例化对象的getName函数,因为在Foo构造函数中没有为实例化对象添加任何属性,所以到当前对象的原型对象(prototype)中寻找getName,找到了,执行,alert 3;

 第七问:new new Foo().getName(); 正确打开方式:new (new (Foo().getName());跟第六问一样,先初始化Foo的实例化对象,然后对其原型上的getName函数作为构造函数再次new,最终结果是3。

原文地址:https://www.cnblogs.com/Decmber/p/5280600.html