//////////////////////
这道“七连击”的前端面试题来自与Github上的一篇文章,已经火了很久。介于这道题很经典,考点还很多,所以在这里与大家分享。
格式化后的代码,看上去会舒服一些
先看此题的上半部分做了什么,首先定义了一个叫Foo的函数,之后为Foo创建了一个叫getName的静态属性存储了一个匿名函数,之后为Foo的原型对象新创建了一个叫getName的匿名函数。之后又通过函数变量表达式创建了一个getName的函数,最后再声明一个叫getName函数。
Q
第一问
Foo.getName()
答案:2
详解:第一问的Foo.getName自然是访问Foo函数上存储的静态属性,答案自然是2,这里就不需要解释太多的,一般来说第一问对于稍微懂JS基础的同学来说应该是没问题的,当然我们可以用下面的代码来回顾一下基础,先加深一下了解
注意下面这几点:
● 调用公有方法,公有属性,我们必需先实例化对象,也就是用new操作符实化对象,就可构造函数实例化对象的方法和属性,并且公有方法是不能调用私有方法和静态方法的
● 静态方法和静态属性就是我们无需实例化就可以调用
● 而对象的私有方法和属性,外部是不可以访问的
//////////////////////
Q
第二问
getName();
答案:4
详解:第二问,直接调用getName函数。既然是直接调用那么就是访问当前上文作用域内的叫getName的函数,所以这里应该直接把关注点放在4和5上,跟1 2 3都没什么关系。当然后来我问了我的几个同事他们大多数回答了5。此处其实有两个坑,一是变量声明提升,二是函数表达式和函数声明的区别。
函数声明
函数表达式
先看下面这个经典问题,在一个程序里面同时用函数声明和函数表达式定义一个名为getName的函数
上面的代码看起来很类似,感觉也没什么太大差别。但实际上,Javascript函数上的一个“陷阱”就体现在Javascript两种类型的函数定义上。
● JavaScript 解释器中存在一种变量声明被提升的机制,也就是说函数声明会被提升到作用域的最前面,即使写代码的时候是写在最后面,也还是会被提升至最前面。
● 而用函数表达式创建的函数是在运行时进行赋值,且要等到表达式赋值完成后才能调用
所以可以分解为这两个简单的问题来看清楚区别的本质
这个区别看似微不足道,但在某些情况下确实是一个难以察觉并且“致命“的陷阱。出现这个陷阱的本质原因体现在这两种类型在函数提升和运行时机(解析时/运行时)上的差异。 当然我们给一个总结:Javascript中函数声明和函数表达式是存在区别的,函数声明在JS解析时进行函数提升,因此在同一个作用域内,不管函数声明在哪里定义,该函数都可以进行调用。而函数表达式的值是在JS运行时确定,并且在表达式赋值完成后,该函数才能调用。 所以第二问的答案就是4,5的函数声明被4的函数表达式覆盖了
//////////////////////
Q
第三问
Foo().getName();
答案:1
Foo().getName();先执行了Foo函数,然后调用Foo函数的返回值对象的getName属性函数。 Foo函数的第一句getName = function () { alert (1); };是一句函数赋值语句,注意它没有var声明,所以先向当前Foo函数作用域内寻找getName变量,没有。再向当前函数作用域上层,即外层作用域内寻找是否含有getName变量,找到了,也就是第二问中的alert(4)函数,将此变量的值赋值为function(){alert(1)}。 此处实际上是将外层作用域内的getName函数修改了。
注意:此处若依然没有找到会一直向上查找到window对象,若window对象中也没有getName属性,就在window对象中创建一个getName变量。
之后Foo函数的返回值是this,而JS的this问题已经有非常多的文章介绍,这里不再多说。 简单的讲,this的指向是由所在函数的调用方式决定的。而此处的直接调用方式,this指向window对象。 遂Foo函数返回的是window对象,相当于执行window.getName(),而window中的getName已经被修改为alert(1),所以最终会输出1 此处考察了两个知识点,一个是变量作用域问题,一个是this指向问题 我们可以利用下面代码来回顾下这两个知识点
因为JS没有块级作用域,但是函数是能产生一个作用域的,函数内部不同定义值的方法会直接或者间接影响到全局或者局部变量,函数内部的私有变量可以用闭包获取,函数还真的是第一公民呀~ 而关于this,this的指向在函数定义的时候是确定不了的,只有函数执行的时候才能确定this到底指向谁,实际上this的最终指向的是那个调用它的对象 所以第三问中实际上就是window在调用**Foo()**函数,所以this的指向是window
//////////////////////
Q
第四问
getName();
答案:1
直接调用getName函数,相当于
window.getName(),因为这个变量已经被Foo函数执行时修改了,遂结果与第三问相同,为1,也就是说Foo执行后把全局的getName函数给重写了一次,所以结果就是Foo()执行重写的那个getName函数
Q
第五问
new Foo.getName();
答案:2
第五问new Foo.getName();此处考察的是JS的运算符优先级问题,我觉得这是这题灵魂的所在,也是难度比较大的一题 下面是JS运算符的优先级表格,从高到低排列。可参考MDN运算符优先级
所以new Foo.getName();的优先级相当于下面的样子:
● 点的优先级比new无参数列表优先级高
● 当点运算完后又因为有个括号(),此时就是变成new有参数列表,所以直接执行new,当然也可能有朋友会有疑问为什么遇到()不函数调用再new呢,那是因为函数调用比new有参数列表优先级低
所以这里实际上将getName函数作为了构造函数来执行,遂弹出2。
+
Q
您还谈到了荧幕中的中美女性形象,在中国最受欢迎的是白百何那样的,菜鸟、迷糊、男孩气、莽撞、小可爱;而备受美国青年人追捧的女性是《饥饿游戏》里的詹妮弗·劳伦斯,坚韧、果敢、女性领袖的形象。(《中国男人为什么喜欢白百何这样的姑娘》)这种对于女性的审美差异从何而来?