一道考察运算符优先级的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();

问题:页面会弹出数字几?以及为什么?

麻烦前端er帮我解答一下,谢谢。
运算符优先级参考列表:MDN

---------------- 2017/2/22/10:18更新 ----------------

  • 昨天问了这个问题后,受到了@xiaoboost 的嘲讽,立马感觉到自己没有先找找类似的问题就提问,挺鲁莽的。所以立即选择了关闭问题(选择与社区已有问题重复)。

  • 但是经过昨天晚上的查找以及看了@xiaoboost 的答案后,发现很多答案并不对。这道面试的部分问题确实牵扯到了运算符的优先级。所以我给@xiaoboost 的答案投了反对票。

  • 另外,由于我去掉了原面试题的部分代码,造成了@zp1996 的困扰,抱歉。现在已经补全了完整的面试题。

---------------- 2017/2/22/16:28更新 ----------------
抛开面试会不会出这个题,只谈这道题所牵扯到的知识点来说,大家都觉得和运算符优先级没有关系。
那么请问第六问:new Foo().getName()为什么先执行 new Foo()呢?属性访问器与带参数的对象创建表达式优先级是一样的。为什么不是先执行Foo().getName呢?

大家可以看个比较靠谱的回答,当然第六问解释的还不够严谨。
https://segmentfault.com/a/11...

最初的问题是问:new Foo().getName();这一句弹出什么数字以及为什么?我认为这句的问题和运算符的优先级肯定有关系。

我希望大家理性一些,不要急着踩我。

---------------- 2017/2/22/17:13更新 ----------------
这里是出这道题的原作者,文章中有对这道题的解释。从第五问开始就与运算符的优先级有关系了。但是第六问他解释错了,new Foo().getName()中并没有圆括号,所以并不存在圆括号优先级高于成员访问.
http://www.cnblogs.com/xxcang...

---------------- 可能是最后一次更新 ----------------

各位“踩”我问题朋友,我发表了一篇专门针对这个问题的文章,欢迎各位前往指出文章错误的地方,谢谢。
传送门:https://segmentfault.com/a/11...

阅读 8.4k
4 个回答

我个人是不太喜欢这种问题的,但需要解决这个问题确实需要一些基本功。一般来说,程序写多了,自然记住了什么情况下会发生什么样的事情,但是为什么会发生这样的事情,可能问起来一时还真回答不了!

另外,我认为需要对这个问题平个反——它确实涉及到了运算优先级。new 是运算符、成员访问是运算(. 运算符)、函数调用是运算……而且题主也给出了 MDN 的链接,大家可以看到这个问题中的运算优先级基本上集中 17、18、19 上,都是很高的优先级。

clipboard.png

关于优先级,这里需要说明的一点是,表中同一优先级下的多个运算是没有优先顺序的,通常是先遇到哪个就先运算哪个,所以说成员访问优先于 new xxx() 并无道理,但是成员访问的确优先于 new xxx——OK,这里遇到了我认为这个题中是难理解的地方:new Foo.getName()new Foo().getName(),因为 new 的两种形式有两个不同的优先级。

new 的两种形式的运算,一个是带括号的,称为带参数列表的 new,优先级18;另一种是不带括号的,称为无参数列表的 new,优先级 17。

所以,虽然 new Foo 是合法的构造运算,但是 new Foo.getName() 却是先运算了 Foo.getName 这个成员访问运算。为什么呢?因为如果 new Foo 看作一个运算,它的优先级是 17,低于成员访问运算,所以应该先计算 Foo.getName,对其计算结果再进行 new XXX() 运算。

其它问题都比较容易理解,我就懒得解释了。希望大家在解决问题的时候尽量少带情绪(我承认我也经常带情绪,但是俗话说:淡定!)

只能说出这题的公司真是太菜了,这题完全问的扯淡啊,一堆代码,毫无意义,最后肯定是执行原型上的方法,肯定是3,是不是题主把这道题没有看全啊,这么考一点意义没有。

看标注吧,另外这个和运算符毫无关系,题主还是不要看这些面试题了,因为题主离面试差的太远了(首先弄清楚什么叫运算符吧),建议题主在学习前端之前先学学C++,这样就知道什么叫运算符了。

function Foo() {
  // Foo执行后覆盖全局变量getName
  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();              // 2
getName();                  // 4
Foo().getName();            // 1,this指向全局
getName();                  // 1
new Foo.getName();          // 2
new Foo().getName();        // 3
new new Foo().getName();    // 3,这么看new (new Foo()).getName()

这是我在SF第6次看到这个问题了………………
这个问题和运算符优先级没有半毛钱关系,考察的是对象属性对象原型属性构造函数以及new运算符做了什么,这四个问题。


翻到了以前的问题,你自己参考着看吧。
求出此面试题的人员的心态!

链接里面的程序用的是console.log,和你这里的alert不太一样,不过整体过程和道理都是差不多的。

分别说一下这4个getName的区别

  1. getName被赋值了一个匿名函数,但没有var声明,最后被定义到了全局变量中。
    这时候由于没有触发Foo(),不会弹出内容

  2. getName被定义到了Foo的原型上,这时候如果想调用getName需要实例Foo。

    var foo = new Foo();
    foo.getName()

  3. 在最外层声明了一个getName变量,getName() 此时弹出 4

  4. 声明一个函数getName,会被提升到这些代码之前,此时getName() 弹出依旧为 4

so, new Foo() 先创建实例,然后调用 getName(),结果为 3

顺便一说,new Foo() 的操作会触发内部的 getName 赋值,之后的单独调用 getName() 会弹出 1。

注: 这题不是关于运算符优先级的,不要搞错了

推荐问题
宣传栏