不少初学JavaScript的人对函数的调用理解有些模糊的,话很少说,直接上题:函数
function Foo() { getName = function () { console.log(1); }; return this; } Foo.getName = function () { console.log(2); }; Foo.prototype.getName = function () { console.log(3); }; var getName = function () { console.log(4); }; function getName() { console.log(5); }
请写出如下每行代码的输出结果this
Foo.getName(); getName(); Foo().getName(); getName(); new Foo.getName(); new Foo().getName();
此题涉及的知识点比较普遍,包括变量的做用域、变量提高、this指针指向、运算符优先级、原型、继承、等等。prototype
解题以前,咱们先分析一下代码:指针
//① 函数声明,声明了一个Foo函数,里面有个全局变量getName指向一个匿名函数 function Foo() { getName = function () { console.log(1); }; return this; } //② Foo函数的建立了一个静态属性getName指向一个匿名函数 Foo.getName = function () { console.log(2); }; //③ Foo函数的原型上建立了一个getName方法 Foo.prototype.getName = function () { console.log(3); }; //④ 函数表达式,定义一个变量getName指向一个匿名函数 var getName = function () { console.log(4); }; //⑤ 函数声明,声明了一个叫getName的有名函数 function getName() { console.log(5); }
Foo.getName(); //2
访问Foo函数上存储的静态属性getName,因此结果为2。code
getName(); //4
直接调用getName函数,至关于调用window.getName,那么与函数①、②、③无关。在分析题目前咱们首先知道什么变量提高以及JS引擎工做的执行顺序。对象
变量提高
全部的变量声明都会被提高到它所在做用域的最开始的部分
例如:继承
console.log(a); //undifined var a = 10;
js引擎执行代码的顺序为:ip
var a; console.log(a); a = 10;
例如:作用域
var a = 10; function test(){ conlose.log(a); //undifined var a = 20; } test();
js引擎执行代码的顺序为:get
var a; a = 10; function test(){ var a; conlose.log(a); //undifined a = 20; }
JS引擎工做的执行顺序:
咱们再看原题,函数④的变量会被提高,再结合上述JS引擎工做的执行顺序,所以代码的执行顺序是:
function Foo(){ getName = function(){ console.log(1); }; return this; } var getName; function getName(){ console.log(5); } Foo.getName = function(){ console.log(2); }; Foo.prototype.getName = functio() { console.log(3); }; getName = function(){ console.log(4); //最终的赋值覆盖function getName声明 };
因此结果为4。
Foo().getName(); //1
Foo().getName(); 先执行了Foo函数,而后调用Foo函数的返回值对象的getName属性函数。
先看前面的Foo()
显然调用了函数①,Foo函数第一句getName = function(){alert(1);};是一句赋值语句,由于它没有var声明,因此先向当前Foo函数做用域内寻找getName变量,没有。再向当前函数做用域上层,即外层做用域内寻找是否含有getName变量,找到函数④var getName=function(){alert(1)};,将此变量getName赋值为function(){alert(1)}。
咱们将相关的代码单独拿出:
function Foo() { getName = function () { //全局变量 console.log(1); }; } var getName = function () { console.log(4); }; Foo(); console.log(getName); //function(){console.log(1); }
实质上就是将外层做用域内的getName函数修改了。
此处考察了两个知识点,一个是变量做用域,一个是this指向。
关于变量做用域,若是将代码变为:
function Foo() { var getName = function () { // 局部变量,只能在函数内部访问 console.log(1); }; } var getName = function () { console.log(4); }; Foo(); console.log(getName);//function(){console.log(4);}
那么此题结果则为4了。
getName(); //1
再次调用getName函数时,此时函数④已经被第三问执行Foo()时修改,因此结果为1。
new Foo.getName(); //2
--> new(Foo.getName)()
此处考察的是js的运算符优先级。
.的优先级大于new,所以应该先执行Foo.getName(),那么执行函数②,结果是2,而后new了Foo的实例对象即new(Foo.getName)()。
最终结果为2。
new Foo().getName();//3
--> (new Foo()).getName()
接着咱们就要说到构造函数和原型实例和实例对象。
实例对象可以调用的方法和属性只能是定义在自身函数内方法和继承了构造函数原型中的方法和属性。
实例对象.getName()自己并无定义属性和方法,则继承其构造函数Foo原型中的方法,执行函数③,结果为3。