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