zoukankan      html  css  js  c++  java
  • 前端面试官如何从笔试中选拔人才

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

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

    题目是:定义一个方法,传入一个string类型的参数,然后将string的每个字符间加个空格返回,例如:

    spacify('hello world') // => 'h e l l o  w o r l d'    

    这题我认为是比较简单的,但是考虑到好的考题都是容易的在前,稍难的在中间,最难的在后面。这样布局是很合乎常理的。让面试者有一个热身过程,做到良好发挥。

    因为做为面试官,不是要把面试者都难倒,证明你个人有多牛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应用,栅格布局, 异步编程问题,甚至可以问问算法,数据库什么的。只要给的起价,怎么折腾都行。

  • 相关阅读:
    vue自定义指令
    ZOJ Problem Set–2104 Let the Balloon Rise
    ZOJ Problem Set 3202 Secondprice Auction
    ZOJ Problem Set–1879 Jolly Jumpers
    ZOJ Problem Set–2405 Specialized FourDigit Numbers
    ZOJ Problem Set–1874 Primary Arithmetic
    ZOJ Problem Set–1970 All in All
    ZOJ Problem Set–1828 Fibonacci Numbers
    要怎么样调整状态呢
    ZOJ Problem Set–1951 Goldbach's Conjecture
  • 原文地址:https://www.cnblogs.com/afrog/p/4241670.html
Copyright © 2011-2022 走看看