-
Notifications
You must be signed in to change notification settings - Fork 735
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
前端程序员经常忽视的一个JavaScript面试题 #85
Comments
运算符优先级,之前没有太注意,面试题不错。👍👍 |
感觉这意义不大。。 |
前六个不说了,或许工作中有机会出现,但最后一个给我的感觉就是在扯犊子。 |
第三问中的例子貌似错了: var name = "Wscats";//全局变量
window.name = "Wscats";//全局变量
function getName(name) {
console.log(name); //Hello
// -----------------------------------------------
/*******因为getName中的name有作为形参传了进去,所以就算这里去掉了var,也没办法改变全局变量name,除非去掉了形参name*******************/
name = "Oaoafly"; //去掉var变成了全局变量
// -----------------------------------------------
var privateName = "Stacsw";
return function() {
console.log(this);//window
return privateName
}
}
var getPrivate = getName("Hello"); //传参是局部变量
console.log(name) //Oaoafly
console.log(getPrivate()) //Stacsw 嗯嗯,是的当时在写例子的时候没注意,加上形参name这个知识点之后会影响到函数内部全局name的修改失效,谢谢指正~已修改了原例子by @Wscats var name = "Wscats"; //全局变量
window.name = "Wscats"; //全局变量
function getName() {
name = "Oaoafly"; //去掉var变成了全局变量
var privateName = "Stacsw";
return function() {
console.log(this); //window
return privateName
}
}
var getPrivate = getName("Hello"); //传参是局部变量
console.log(name) //Oaoafly
console.log(getPrivate()) //Stacsw |
楼上的都过于较真了,作为面试题,不是说让面试者把题都答对,而是要考察他了不了解这个知识点。从而看出他对编程思想的理解。可能在业务上很少有人这么写程序,但是考察应试者的编程素养还是可以的。全答对了,也有可能是蒙的。全答错了,也未必一无是处,只要他懂这个原理就可以了 |
通过这个练习可以加深对基础的夯实咯 |
嗯,面试的时候,不要求你一定要答对,(当然答对更好,蒙的也可以,呵呵)主要是看看你是否了解其中的原理与思想,答对与答错对面试官来说,这不是主要的。 |
去年第一次看觉得 哇 好高深 这次遇到 就觉得好简单 |
这尼玛,感觉在玩脑精,,急转弯 |
去年第一次看觉得 哇 好高深 这次遇到 就觉得好简单 +1 |
加深基础 |
语言的缺陷被称之为‘基础’~ |
巩固知识,不错不错 |
语言的缺陷被称之为基础~ |
语言的缺陷被称之为‘基础’,2333,你们够了 |
考优先级有什么用? |
在这个页面搜索 |
亲们, 不要说什么语言的缺陷, 考优先级有什么用, 实际工作中也不需要, blabla, 这就是套路! |
原来我还是小白😂 |
原来我也是小白:joy: |
我的天啊 这才是基础 |
嗯。。优先级这块真面试,我也不能记住所有运算符优先级,其他知识点都挺合理的 |
虽然很想吐槽这宛如谭浩强的题目,不过还是要感谢 lz 的细心总结! |
很反感这种考优先级的题目,前四个的知识点很棒。之后开始优先级我就真的恶心。我觉得业务中大家都是加括号来搞定。你写这种说不定老板都要叫你滚。对于常见必用的优先级脑子想想就知道的考了没意义。太难的考了根本不用上就如我上面说的。单纯给我感觉就是为了考你而考你。而不是真的弄出一个知识点。你干脆考我其他语言好了。那个还能考察我知识的广度。又或是考框架源码,那个涉及我知识的深度。这优先级是为了什么,嗯? 还两个new。一辈子都碰不到。碰到了我只能说程序员职业消失了。 |
明显第三问的答案应该是4,函数表达式会覆盖掉前面的所有getName的定义 |
今天在公众号上看到这个。 |
你好,我是关注「高级前端进阶」的号主,觉得你的文章蛮不错的,是否可以转载你的这篇文章,会注明作者和来源,谢谢~ @yygmind 可以的 |
原作者是小小沧海吧,怎么不看出处就转载别人的东西?文末标记的也不明显 @Tisiven 这个我文中也说明了是面试中遇到的面试题,所以这道题原题不是我的,我也在文末注明了此处 |
很棒 |
原来我还是小白 |
整吐了呀 |
我在MDN上运行了全部代码 |
现代浏览器已经让人写不出这样的代码了 |
题目
这几天面试上几次碰上这道经典的题目,特地从头到尾来分析一次答案,这道题的经典之处在于它综合考察了面试者的JavaScript的综合能力,包含了变量定义提升、this指针指向、运算符优先级、原型、继承、全局变量污染、对象属性及原型属性优先级等知识,此题在网上也有部分相关的解释,当然我觉得有部分解释还欠妥,不够清晰,特地重头到尾来分析一次,当然我们会把最终答案放在后面,并把此题再改高一点点难度,改进版也放在最后,方便面试官在出题的时候有个参考,更多详情可关注本文作者@Wscats
第一问
先看此题的上半部分做了什么,首先定义了一个叫Foo的函数,之后为Foo创建了一个叫getName的静态属性存储了一个匿名函数,之后为Foo的原型对象新创建了一个叫getName的匿名函数。之后又通过函数变量表达式创建了一个getName的函数,最后再声明一个叫getName函数。
第一问的Foo.getName自然是访问Foo函数上存储的静态属性,答案自然是2,这里就不需要解释太多的,一般来说第一问对于稍微懂JS基础的同学来说应该是没问题的,当然我们可以用下面的代码来回顾一下基础,先加深一下了解
注意下面这几点:
第二问
第二问,直接调用getName函数。既然是直接调用那么就是访问当前上文作用域内的叫getName的函数,所以这里应该直接把关注点放在4和5上,跟1 2 3都没什么关系。当然后来我问了我的几个同事他们大多数回答了5。此处其实有两个坑,一是变量声明提升,二是函数表达式和函数声明的区别。
我们来看看为什么,可参考(1)关于Javascript的函数声明和函数表达式 (2)关于JavaScript的变量提升
在Javascript中,定义函数有两种类型
函数声明
函数表达式
先看下面这个经典问题,在一个程序里面同时用函数声明和函数表达式定义一个名为getName的函数
上面的代码看起来很类似,感觉也没什么太大差别。但实际上,Javascript函数上的一个“陷阱”就体现在Javascript两种类型的函数定义上。
所以可以分解为这两个简单的问题来看清楚区别的本质
这个区别看似微不足道,但在某些情况下确实是一个难以察觉并且“致命“的陷阱。出现这个陷阱的本质原因体现在这两种类型在函数提升和运行时机(解析时/运行时)上的差异。
当然我们给一个总结:Javascript中函数声明和函数表达式是存在区别的,函数声明在JS解析时进行函数提升,因此在同一个作用域内,不管函数声明在哪里定义,该函数都可以进行调用。而函数表达式的值是在JS运行时确定,并且在表达式赋值完成后,该函数才能调用。
所以第二问的答案就是4,5的函数声明被4的函数表达式覆盖了
第三问
Foo().getName();
先执行了Foo函数,然后调用Foo函数的返回值对象的getName属性函数。Foo函数的第一句
getName = function () { alert (1); };
是一句函数赋值语句,注意它没有var声明,所以先向当前Foo函数作用域内寻找getName变量,没有。再向当前函数作用域上层,即外层作用域内寻找是否含有getName变量,找到了,也就是第二问中的alert(4)函数,将此变量的值赋值为function(){alert(1)}
。此处实际上是将外层作用域内的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
第四问
直接调用getName函数,相当于
window.getName()
,因为这个变量已经被Foo函数执行时修改了,遂结果与第三问相同,为1,也就是说Foo执行后把全局的getName函数给重写了一次,所以结果就是Foo()执行重写的那个getName函数第五问
第五问
new Foo.getName();
此处考察的是JS的运算符优先级问题,我觉得这是这题灵魂的所在,也是难度比较大的一题下面是JS运算符的优先级表格,从高到低排列。可参考MDN运算符优先级
这题首先看优先级的第18和第17都出现关于new的优先级,new (带参数列表)比new (无参数列表)高比函数调用高,跟成员访问同级
new Foo.getName();
的优先级是这样的相当于是:
()
,此时就是变成new有参数列表(18),所以直接执行new,当然也可能有朋友会有疑问为什么遇到()不函数调用再new呢,那是因为函数调用(17)比new有参数列表(18)优先级低所以这里实际上将getName函数作为了构造函数来执行,遂弹出2。
第六问
这一题比上一题的唯一区别就是在Foo那里多出了一个括号,这个有括号跟没括号我们在第五问的时候也看出来优先级是有区别的
那这里又是怎么判断的呢?首先new有参数列表(18)跟点的优先级(18)是同级,同级的话按照从左向右的执行顺序,所以先执行new有参数列表(18)再执行点的优先级(18),最后再函数调用(17)
这里还有一个小知识点,Foo作为构造函数有返回值,所以这里需要说明下JS中的构造函数返回值问题。
构造函数的返回值
在传统语言中,构造函数不应该有返回值,实际执行的返回值就是此构造函数的实例化对象。
而在JS中构造函数可以有返回值也可以没有。
原题中,由于返回的是this,而this在构造函数中本来就代表当前实例化对象,最终Foo函数返回实例化对象。
之后调用实例化对象的getName函数,因为在Foo构造函数中没有为实例化对象添加任何属性,当前对象的原型对象(prototype)中寻找getName函数。
当然这里再拓展个题外话,如果构造函数和原型链都有相同的方法,如下面的代码,那么默认会拿构造函数的公有方法而不是原型链,这个知识点在原题中没有表现出来,后面改进版我已经加上。
第七问
new new Foo().getName();
同样是运算符优先级问题。做到这一题其实我已经觉得答案没那么重要了,关键只是考察面试者是否真的知道面试官在考察我们什么。最终实际执行为:
先初始化Foo的实例化对象,然后将其原型上的getName函数作为构造函数再次new,所以最终结果为3
答案
后续
后续我把这题的难度再稍微加大一点点(附上答案),在Foo函数里面加多一个公有方法getName,对于下面这题如果用在面试题上那通过率可能就更低了,因为难度又大了一点,又多了两个坑,但是明白了这题的原理就等同于明白了上面所有的知识点了
最后,其实我是不建议把这些题作为考察面试者的唯一评判,但是作为一名合格的前端工程师我们不应该因为浮躁忽略了我们的一些最基本的基础知识,当然我也祝愿所有面试者找到一份理想的工作,祝愿所有面试官找到心中那匹千里马~
参考文档
The text was updated successfully, but these errors were encountered: