前端面試官如何從筆試中選拔人才

    我沒有參加過一線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應用,柵格佈局, 異步編程問題,甚至能夠問問算法,數據庫什麼的。只要給的起價,怎麼折騰都行。

相關文章
相關標籤/搜索