前端面试官如何从笔试中选拔人才

    我没有参加过一线IT公司的笔试,可是我据说大公司都喜欢笔试,因而从网上搜了一些面试方面的题来试手。不管是笔试仍是被笔试,有备无惧嘛。我我的倾向于javascript编程方向的开发,因此面试题天然也是选择这方面相关的。网上各种面试题的质量良莠不齐,其中有一篇叫《如何面试前端工程师》的博文里边给的题型是我认为最好的。题量不多,可是考察的方向都基本上覆盖了。做者说他是个行业派(言下之意他也是写代码的),注重前端编程的能力。下面我就拿它的题来分析一下:javascript

第一部分:Object Prototypes (对象原型)

题目是:定义一个方法,传入一个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的经典用法,若是不是有多年的编程经验,是很难领会这一行代码的。固然,若是说你是背出来的,另当别论了。数组

第二部分:apply和call的用法

定义一个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应用,栅格布局, 异步编程问题,甚至能够问问算法,数据库什么的。只要给的起价,怎么折腾都行。

相关文章
相关标签/搜索