一道日常的面试题被鄙视引发的较真o( ̄ヘ ̄o#)

原因

开通文章是为了可以有个地方长篇大论今天遇到的问题
因为提了一个问题(见 这里),被人嘲讽。可是这个嘲讽个人人(@xiaoboost )的答案并不对,他的答案只是根据结果解释可以得出这个结果的执行。至于为何以及JavaScript在执行过程当中进行了哪些判断并无进行详细的解释或者说完整的解释。其他的几个朋友要么鄙视这道题、要么鄙视我把运算符优先级牵扯进来。最让我纳闷的是你们都认为这里面不牵扯到运算符的优先级判断。
在此,我以本身的理解来解释一下JavaScript在执行过程当中进行了哪些判断和操做。javascript

原始问题:

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()

个人答案:

首先,JS中会对变量声明和函数声明在编译阶段进行提高,因此实际代码会表现成这样:html

// 此处是变量提高的演示,是在编译阶段进行的
var getName;
function Foo() {
  getName = function () {
    alert(1);
  };
  return this;
}
function getName() {
  alert(5);
}

// 此处则只有到了执行阶段才会执行
Foo.getName = function () {
  alert(2);
};
Foo.prototype.getName = function () {
  alert(3);
};
getName = function () {
  alert(4);
};

其次,除了第六问和第七问,全部有成员访问运算符(也叫属性访问器).的表达式,.的优先级都是最高,没有一个表达式有圆括号,有圆括号的要么是函数调用,要么是new 运算(对象建立表达式)的组成部分。java

这里专门说明没有圆括号是由于看到这个问题原做者本身的分析文章将new Foo().getName()中的new Foo()先执行解释为因为圆括号的优先级高于.。附上问题原做者本身的分析文章:http://www.cnblogs.com/xxcang...segmentfault

如下为各问题的数字输出以及为何:函数

第一问

2,直接调用Foo函数的getName方法。this

第二问

4,变量声明和函数声明会在编译阶段被提高。此时,函数声明会覆盖变量声明。可是到了执行阶段,若是变量有赋值操做,那么变量会因赋值而覆盖以前的函数声明。所以第二问的getName()实际执行的是赋值了匿名函数function () {alert(4)}的函数表达式。prototype

第三问

1,.运算符优先级最高,按照.运算符的关联性从左往右先计算左操做数,Foo()是一个函数调用表达式,函数内部在执行时,因为函数内部没有查找到局部变量getName,所以引擎会沿着做用域链向上查找getName变量。在全局做用域中找到getName变量(同时也是window的属性/方法),给它赋值一个新的匿名函数function(){alert(1)}returnthis此时指向当前方法所属的对象window,所以,计算右操做数时,就是调用了window对象上的getName方法。而前面左操做数Foo()中已经给getName变量从新赋值了一个匿名函数,所以会出现新赋值函数所弹出的数字1。code

第四问

1,getName()已经由于前面的Foo()的调用而赋值了新的匿名函数,所以弹出数字1。htm

第五问

2,new能够与Foo结合组成一个没有参数的对象建立表达式,可是这样它的优先级低于'.',所以这里.运算符优先级高于new和函数调用(),整个表达式则能够理解为:new (Foo.getName)(),当Foo.getName执行完毕会返回一个匿名函数function(){alert(2)},此时会与new运算符、()组成一个新的表达式:new function(){alert(2)}(),这是一个对象建立表达式,这个表达式在执行执行的时候,构造函数部分function(){alert(2)}会被执行,结果就是2。另外,对象建立表达式在没有传入参数的状况下能够省略括号,所以原题中的new Foo.getName();能够把后面的括号省略掉new Foo.getName,结果同样。对象

第六问

3,带有参数的对象建立表达式(new constructor())和成员访问表达式的优先级是同样,这里就牵扯到应该理解成
(new Foo()).getName()仍是new (Foo().getName)()。若是是new (Foo().getName)(),那么在执行Foo()时,它是一个函数调用,优先级低于带参数的new,所以这样不行。那么只能是(new Foo()).getName()new Foo()实例化一个对象,而后经过.访问getName属性,对象自己没有这个属性,顺着原型链查找到Foo.prototype中有这个属性,而且赋值了一个匿名函数。最后经过函数调用运算符调用它,获得3。

第七问

3,能够看作 new((new Foo()).getName)()(new Foo()).getName同上面的结果同样获得Foo.prototype.getName一个匿名函数:function () {alert(3)},这个匿名函数与前面的new以及后面的()组成一个新的对象建立表达式new function () {alert(3)} (),这个表达式在执行时,会调用其中的构造函数function (){alert(3)},所以会弹出3。

以上是我对这个问题的解释,其中第六问获得@zonxin 的解答,再次感谢,附上地址:https://segmentfault.com/q/10...

最后,附上我被嘲讽而且被4我的'踩'的问题地址,若是以为个人解答是正确的,麻烦帮我'平反'╥﹏╥...;若是有错误的地方,请留言指出,我会十分感谢。
个人原问题地址:https://segmentfault.com/q/10...

相关文章
相关标签/搜索