摘要:第七问,可以看做,同上面的结果一样得到一个匿名函数,这个匿名函数与前面的以及后面的组成一个新的对象创建表达式,这个表达式在执行时,会调用其中的构造函数,因此会弹出。
缘由
开通文章是为了能够有个地方长篇大论今天遇到的问题
由于提了一个问题(见 这里),被人嘲讽。但是这个嘲讽我的人(@xiaoboost )的答案并不对,他的答案只是根据结果解释能够得出这个结果的执行。至于为什么以及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中会对变量声明和函数声明在编译阶段进行提升,所以实际代码会表现成这样:
// 此处是变量提升的演示,是在编译阶段进行的 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 运算(对象创建表达式)的组成部分。
这里专门说明没有圆括号是因为看到这个问题原作者自己的分析文章将new Foo().getName()中的new Foo()先执行解释为由于圆括号的优先级高于.。附上问题原作者自己的分析文章:http://www.cnblogs.com/xxcang...
以下为各问题的数字输出以及为什么:
第一问2,直接调用Foo函数的getName方法。
第二问4,变量声明和函数声明会在编译阶段被提升。此时,函数声明会覆盖变量声明。但是到了执行阶段,如果变量有赋值操作,那么变量会因赋值而覆盖之前的函数声明。因此第二问的getName()实际执行的是赋值了匿名函数function () {alert(4)}的函数表达式。
第三问1,.运算符优先级最高,按照.运算符的关联性从左往右先计算左操作数,Foo()是一个函数调用表达式,函数内部在执行时,由于函数内部没有查找到局部变量getName,因此引擎会沿着作用域链向上查找getName变量。在全局作用域中找到getName变量(同时也是window的属性/方法),给它赋值一个新的匿名函数function(){alert(1)}。return的this此时指向当前方法所属的对象window,因此,计算右操作数时,就是调用了window对象上的getName方法。而前面左操作数Foo()中已经给getName变量重新赋值了一个匿名函数,因此会出现新赋值函数所弹出的数字1。
第四问1,getName()已经因为前面的Foo()的调用而赋值了新的匿名函数,因此弹出数字1。
第五问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...
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/81681.html
摘要:面试会进入下一个环节。如果在页面中有多个按钮,那么这个弹出窗要如何实现同上,根据之前的建议在回答问题之前要问清楚问题中模棱两可的地方。是否触发不同按钮弹出的窗口现实的内容不同。 这道面试题,当初我面试的时候被问过两次,因此比较深,此外,我记得还有设计模式的考察,所以,有深刻的体会。 面试题主要考察什么 面试不是个轻松的活,不管是对面试官还是面试者都一样。对于面试官来说,别的先不管,首先...
摘要:然后最外层这个函数会返回一个新对象,对象里面有一个属性,名为,而这个属性的值是一个匿名函数,它会返回。 最近看到一条有意思的闭包面试题,但是看到原文的解析,我自己觉得有点迷糊,所以自己重新做一下这条题目。 闭包面试题原题 function fun(n, o) { // ① console.log(o); return { // ② fun: function(m) ...
摘要:说明最近看到这样一段代码问三行的输出分别是什么觉得有点意思,和大家一起来聊聊。说到这里,这道题基本上可以解决了,希望大家能听明白我上面说的话,下面的就简单了。 说明 最近看到这样一段代码 function fun(n,o){ console.log(o); return { fun:function(m){ return fun...
摘要:说明最近看到这样一段代码问三行的输出分别是什么觉得有点意思,和大家一起来聊聊。说到这里,这道题基本上可以解决了,希望大家能听明白我上面说的话,下面的就简单了。 说明 最近看到这样一段代码 function fun(n,o){ console.log(o); return { fun:function(m){ return fun...
阅读 1025·2021-11-22 13:53
阅读 1576·2021-11-17 09:33
阅读 2372·2021-10-14 09:43
阅读 2834·2021-09-01 11:41
阅读 2262·2021-09-01 10:44
阅读 2903·2021-08-31 09:39
阅读 1442·2019-08-30 15:44
阅读 1852·2019-08-30 13:02