我没有参加过一线IT公司的笔试,可是我据说大公司都喜欢笔试,因而从网上搜了一些面试方面的题来试手。不管是笔试仍是被笔试,有备无惧嘛。我我的倾向于javascript编程方向的开发,因此面试题天然也是选择这方面相关的。网上各种面试题的质量良莠不齐,其中有一篇叫《如何面试前端工程师》的博文里边给的题型是我认为最好的。题量不多,可是考察的方向都基本上覆盖了。做者说他是个行业派(言下之意他也是写代码的),注重前端编程的能力。下面我就拿它的题来分析一下:javascript
题目是:定义一个方法,传入一个string类型的参数,而后将string的每一个字符间加个空格返回,例如:前端
spacify('hello world') // => 'h e l l o w o r l d'
这题我认为是比较简单的,可是考虑到好的考题都是容易的在前,稍难的在中间,最难的在后面。这样布局是很合乎常理的。让面试者有一个热身过程,作到良好发挥。java
由于作为面试官,不是要把面试者都难倒,证实你我的有多牛X,而是要选出合适的人才,为企业创造价值。好像说的有点远了,继续分析试题。面试
我一会儿就想出好几个方法,可是保险起见,我用了这个:算法
function spacify(str) { return str.split('').join(' '); }
接下来,难度加大,如何把这个方法放入String对象上面,例如:数据库
'hello world'.spacify();
显然这个问题是要考察侯选人是否对function prototypes(方法原型)有一个基本的理解。我把上面的方法简单改了一下就出来了:编程
String.prototype.spacify = function(){ //可否理解这里的this 是本题的关键 return this.split('').join(' '); };
我认为这题即考察了原型对象,又考察了this的经典用法,若是不是有多年的编程经验,是很难领会这一行代码的。固然,若是说你是背出来的,另当别论了。数组
定义一个log函数,它能够代理console.log的方法。这个简单,再基础不过了:浏览器
function log(msg) { console.log(msg); }
我发现,越是简单的题,我越当心,总要多看几遍,身怕里边有什么坑在等着我跳,仔细看了三遍,确实是没有地雷. 前端工程师
接着题目难度加大,要求能够打印多个参数,参数个数不肯定,舒适提示可使用apply。
既然提示都这么明显了,用apply是没错的:
function log(){ console.log.apply(console, arguments); };
这里要注意的是,用apply的时候,第一个参数是log执行环境的this指向。第二个参数必须是数组,或者是arguments这样的类数组对象。对于这个题目来讲,若是第一个参数用
this的话,指的是在window中去找log这个方法, 那不成了本身找本身啊。
接下来要给每个log消息添加一个"(app)"的前辍,好比:
log('hello world'); //'(app) hello world'
如今可能有点麻烦了。好的侯选人知道arugments是一个伪数组,而后会将他转化成为标准数组。一般方法是使用Array.prototype.slice,像这样:
function log(){ var args = Array.prototype.slice.call(arguments); args.unshift('(app)'); console.log.apply(console, args); };
固然你也能够不用call或apply之类的方法实现,可是按照出题者的意思走,能够收到事半功倍的效果. 若是是技术官,他会认为你懂他!若是不是,他会认为你的答案很贴合“标准答案”。
关于this的身份,一直以来都是各大面试题喜欢八卦的命题。此次也不例外,题目是一段代码:
var User = { count: 1, getCount: function() { return this.count; } };
下面几行,log输出的会是什么?
console.log(User.getCount()); var func = User.getCount; console.log(func());
对于一个javascript 编程的老手来讲,这种状况下,正确的答案是1和undefined,一点都不会意外的,可是对于之前一直是从事套站写网页模版的候选人来讲,就未必了。
固然,我在此并无要黑这些资深网页制做者的意思。
那么问题来了,怎么让这个答案输出都是1呢?
正确的答案是使用Function.prototype.bind,例如:
var func = User.getCount.bind(User); console.log(func());
用bind?这些从ie5时代走过来的前辈们会以为难以接受。这个方法对老版本的浏览器不起做用,好吧,那就顺道问下,这个怎么兼容这个bind好了。
聊到兼容,理论派或者说思路派就要开始口若悬河了,balabala...讲一大堆。他们甚至不屑于写这样的代码,认为过低估了他们的实力。
为了这篇文章的严谨和真实性,我特地拿到我所在的前端开发群里考察了一翻,注意,这个群里边有许多“XX神之类的人物”哦,固然也有我本身。
一块儿来看看它们的答案吧:
杭州的朋友给的答案:
这个暂且不说他写的对与错,这逻辑就有点让人眼晕,直接放弃。过后我采访他,原来是从网上copy的,这种不通过本身吸取的拿来主义,吃再多也是长不胖滴。
再看一位广州的朋友给的答案:
Function.prototype.binds=function(obj){ var fc=this; return function(){ fc.call(obj); } }
再看一位烟台的朋友:
下面说下个人理由:
虽然广州的朋友修改过屡次仍然没有全对,可是都是粗心或是考虑不全面,假以时间,仍是能够胜任工做。
而烟台的那位虽然写的很简洁,也很是注意命名规范,思路也明确,可是Function.apply这一行,暴露了他基础的不足,须要学习的时间成本明显要多一些。固然若是悟性很好的话,另当别论。
以上纯属娱乐,请看到的朋友不要当真。
下面看下个人参考答案:
Function.prototype.bind = function(context){ var self = this; var arg = [].slice.call(arguments,1); return function(){ return self.apply(context,arg) } }
检测一下:
setTimeout(function(msg){ console.log(this.name,this.age,msg) }.bind({ name : 'frog', age : 18 },'blogs')) var User = { count: 1, getCount: function() { return this.count; } }; var func = User.getCount.bind(User); console.log(func());
上面的结果和使用bind的结是一致的,可是既然是作兼容,这样写,至关于忽略了那些原本就支持bind方法的浏览器了。得修改一下:
Function.prototype.bind = Function.prototype.bind || function(context){ var self = this; var arg = [].slice.call(arguments,1); return function(){ return self.apply(context, arg); }; }
这个地方,能够顺便提问关于闭包的问题。
这个是我本身加的,由于我在工做中,常常会遇到这样或那样的问题,多少都与这些东东粘点边。下面是一个例子:
function say(){ function name(){ console.log('frog') } return name(); function name(){ console.log('hello') } } var name = say();
输出的是hello;我认可,不会有人傻X到写这样无聊的代码。这个题的原型我不记得了,反正大体就是说明这样一个问题。固然关于这个例子你确定还有更好的代码。例如:
function say(){ ok(); //这里会报错 var ok = function(){ console.log('ok') } } //---------------- function say(){ ok(); //这样就正常了 function ok(){ console.log('ok') } }
变量声明则不会被提高,函数式声明则会提高。再看一个例子:
var name = 'frog' function hello(){ alert(name); // undefined var name = 'bbc'; }
不少初学者都在这里翻船的。缘由在于他们只记得没有块级做用域,可是没理解什么叫块级做用域。
在javascript中,函数是能够造成一个独立做用域的,变量的查找,首先是就近原则,先看本身有没有,本身没有,就会自动跑到外层去找,这一点和其它语言可能不同,它会自动跑外边去找。在整个hello做用域内,只要定义了name这个变量,就不会去window中找,不过呢,在hello本身的做用域内,还有一个规则,申明以前调用,都是undefined,申明且赋值以后调用才会有值。alert(name)发生在申明以前,因此会弹出undefined就是这么个道理。
下面我再来讲说这个没有块级做用域:
for(var i=0;i<10;i++){ //... } alert(i)//10
这个块,指的就是两个大括号之间的区域, 在javascript中,本来是不存在这个问题的,出现这个疑问,是由那些搞过c语言之类的人转来搞javascript带来的。他们之前的知识
中,循环以后,i自动销毁了,可是javascript中不是这样的。只要记得javascript中,函数才是划分做用域的就能够了。
相似的状况还有判断:
function fn(){if(true){ var i = 10 } return i; }
固然这些问题只能覆盖前端一点点的知识的,还有不少其余的方面你有可能会问到,像性能,HTML5 API, AMD和CommonJS模块模型,构造函数(constructors),类型和盒子模型(box model)。以及热门的移动前端开发框架angula.js,spa应用,栅格布局, 异步编程问题,甚至能够问问算法,数据库什么的。只要给的起价,怎么折腾都行。