杭州17屆前端期待加入一個更好的團隊

趁你還年輕233發表於2018-02-01

最近看到有人分享16屆前端進阿里(前端學習群的群主),17屆前端杭州收割offer(優秀的路人)。 剛好同為17屆的我最近也很輕鬆拿了2個offer,但是我看到他們寫的題目回答並不是很詳細,所以強迫症的我也想來和大家分享下,我會把所有沒掛掉的和掛掉的都分享出來,遇到的每一道題目都做詳細的分析,所以文章有點長哦。

長文慎入哈,但是有酒有故事,小板凳我都給你搬好啦,快坐下!

故事開始咯~

本來想等過完年回來再換工作的,但是前段時間有獵頭在簡書私信我,說看我的文章寫得還行,問我想不想換工作。 然後我就更新了簡歷,發過去了,邀請我面試,但是這家公司在北京 /西安,後來就沒去,但是故事就此開始了,這反而促使我走上了換工作的道路。

貼一下自認為寫得不錯的幾篇文章:

CSS3動畫卡頓效能優化解決方案

深度剖析0.1 +0.2===0.30000000000000004的原因

如何解決0.1 +0.2===0.30000000000000004類問題

記一次簡單的DOM XSS攻擊實驗

你真的理解==和===的區別嗎?

經典CSS坑:如何完美實現垂直水平居中?

自己從大三暑假實習到現在工作半年多,接近快1.5+年的時間,一門心思扎到前端開發領域,高強度式的工作和學習,買書啃書,寫程式碼看程式碼,寫部落格看部落格,提問題回答問題,投入了幾乎是大一到大三學習時間總和的時間學習前端開發。

最近陰差陽錯開始筆試,面試,雖然都沒怎麼準備,但是很輕鬆就收穫了一家阿里子公司,一家浙大系公司的offer,再加上昨天被第三家面試官的讚賞,之前的妄自菲薄一下煙消雲散。

由於自己這次換工作,希望能夠在一家公司呆至少2年以上,非常希望能和一幫對前端技術有見解有思考有追求的小夥伴們,一起去探索鑽研有趣的前端世界。所以我決定嘗試更多的面試,加入到一個更適合自己的團隊。

同時也希望通過這篇面試分享,能夠給和我一樣努力的前端小夥伴們一些鼓勵,也希望能夠把最真實的自己展現給大家。

面試1 - offer1 天道酬勤,付出終有回報

拒絕了第一家主動找到我的公司後,又有家阿里子創業公司在 BOSS 直聘裡私信我,然後抱著測試水平的心態面了。 現場筆試不怎麼樣,但是他們看到我簡書文章還算可以,就讓我重做了一遍題目: 一些特別棒的面試題[0], 最後一道單詞與字串匹配的題的擴充,都是面試官一步步引導我去深入的,感覺學習到很多。

沒想到結果是比較讓人驚喜的,前輩說我的學習能力比較強,所以願意給我這個機會,給的薪資比現在高 2K,關鍵是聽說有許多專案可以做,據說面試我的老闆以前也是在阿里的技術專家。

貼一下面試題和我的回答。

1.說一下你熟悉的設計模式
2.說一下你理解的模組機制
3.MVVM原理
4.最熟悉的框架路由機制
5.狀態管理
6.統計字串中單詞出現次數
複製程式碼

1.說一下你熟悉的設計模式

我最熟悉的設計模式:工廠模式(ES5),元件設計模式(ES6) 工廠模式(ES5,基於prototype。此例中基類Base,子類Factory)

var Factory = function () {
    if(!(this instanceof Factory)){
        return new Factory();
    }
}
Factory.prototype = Object.assign(new Base(), {
    version: '0.0.1',
    defaultOption:{
        title:'標題'
    },
    init:function (cfg) {
        this.title = cfg.title || '';
        this.currentOption = Object.assign(this.defaultOption,{
           //...
        })
    },
    render: function () {
        var option = this.currentOption;
        this.chart.setOption(option);
    },
    showTitle: function () {
        this._showTitle();
    }
})
複製程式碼

元件設計模式(ES6,基於class,方便繼承和初始化,也是React元件的推薦寫法,我比較喜歡。此例中父類Compnent,子類Retrive)

class Retrive extends Component {
    constructor (props) {
        super(props);
        this.state = {
            name:''
        };
        this.getRemoteData = this.getRemoteData.bind(this);
    }
    getRemoteData (data) {
        this.state.retriveResult = data;
    }
    render(){
        return (
            <div className="Retrive">
                <Button name="search" onClick={this.getRemoteData}>查詢</Button>
            </div>
        );
    }
}
複製程式碼

2.說一下你理解的模組機制

AMD: 非同步模組載入規範。 a.js,定義一個依賴jQuery和echrts的元件。

define(['jquery', 'echarts'], function ($, echarts) {
  var AMD = function(){}
  AMD.prototype = {
       title:'',
        foo: function(){}//AMD類或者繼承AMD類的子類的屬性
  }
  function bar(){}//返回,公共屬性
   function baz(){} //未返回,私有屬性
  return {
       main:AMD,
       bar: bar
  }
});

複製程式碼

如果b.js依賴a.js,可以這樣

define(['./a'], function (a) {
     //呼叫建構函式,foo
     var instance_amd = new a.main();
     instance_amd.foo()
      //呼叫bar
     a.bar()
});
複製程式碼

ES6 modules: 和python的包機制很類似,匯入import,匯出export。

1.場景:vue,react推薦機制,需要babel轉義成es5以相容瀏覽器。
2.關於import...(from...)
①.import...from...的from命令後面可以跟很多路徑格式,若只給出vue,axios這樣的包名,則會自動到node_modules中載入;若給出相對路徑及檔案字首,則到指定位置尋找。
②.可以載入各種各樣的檔案:.js、.vue、.less等等。
③.可以省略掉from直接引入。
3.關於export
①.匯出的可以是物件,表示式,函式,類
②.匯出是為了讓別人匯入
4.言外話:使用es6的話,有一個特別好的規範去遵守,airbnb的es6規範(https://github.com/airbnb/javascript)
複製程式碼

CommonJS:nodejs中使用較多,關鍵詞是require,沒寫過node包,只引用過別人的模組,所以內部實現原理不是很清楚。

3.MVVM原理

MVVM是一種軟體架構模式,MVVM有助於前後端分離。 View:檢視層,粗略理解為DOM。 Model:與資料庫對應的model,一般為json格式,作為req的body通過http(s)與資料庫實現通訊。 ViewModel:View與Model通過ViewModel實現雙向繫結。

核心是提供對View和ViewModel的雙向資料繫結,這樣使得ViewModel的改變View立即變化,MVVM在前端的實現有:angular,vue,react。

vue中的常用資料雙向繫結。

view:{{message}}
viewModel v-model="message"
model:message
複製程式碼
<div id="app-6">
  <p>{{ message }}</p>
  <input v-model="message">
</div>

var app6 = new Vue({
  el: '#app-6',
  data: {
         message: 'Hello Vue!'
  }
})
複製程式碼

單檔案元件中的話,就多了一個用html5的template標籤將view和viewModel包裹起來,model部分停留在script標籤部分。

<template>
     view
     viewModel
</tamplate>
<script>
     model
</script>
<styles>
     為了讓view好看點
</styles>
複製程式碼

react的話,我在使用的過程中,沒有聽說過雙向繫結的東西,對redux reducers推薦寫為純函式印象深刻,純函式的話,感覺應該有點單項資料流的意思。

既然說到框架了,說一個最讓我感覺有趣的點,那就是元件間的通訊,對於簡單元件,只涉及父子級別的通訊的,vue使用on emit的方式,react使用props。對於複雜級別通訊,爺爺父親兒子孫子等等時,vue推薦使用vuex,react推薦使用redux,統一的全域性狀態樹用來做狀態管理非常好,可以使得邏輯非常清晰。vue專案檔案結構研究不深,react的專案檔案結構的話,presentational和containers的設計方法感覺非常有道理,一個負責檢視一個負責資料,非常清爽。

4.最熟悉的框架路由機制

vue路由依賴:vue-router 通過組合元件來組成單頁應用程式,只需要將元件對映到路由即可。 前端路由的核心,就在於 —— 改變檢視的同時不會向後端發出請求。 需要注意2種模式的區別:hash模式和history模式,hash模式會在後面加一個很醜的#,可以開啟history去掉。 hash模式原理:它的特點在於:hash 雖然出現在 URL 中,但不會被包括在 HTTP 請求中,對後端完全沒有影響,因此改變 hash 不會重新載入頁面。hash可以理解為錨點,例如./index.html/#/foo,hash值為#/foo,這樣不會跳轉頁面。就相當於統一頁面的不同錨點,頁面間跳轉與 ./index.html/#foo到./index.html/#bar類似。

./store/index.js

import Router from 'vue-router'
Vue.use(Router)
export default new Router({
  routes: [
  {
  path: '/common',
  name: 'common',
  component: Common
  }
]

複製程式碼

路由層面還會包括巢狀路由,動態路由以及重定向,相當於自己模仿瀏覽器請求然後伺服器響應模式,其實不涉及向後端請求,僅在瀏覽器就可以實現頁面跳轉,前段時間我做的使用者許可權控制就用到了vue-router,相比MVC結構下的後端路由,清晰了不少,這樣後端只要負責路由編寫api就好。

5.狀態管理

下面是我在用vuex做專案時的一些思考,簡單修改了一下,也新增了一些關於redux的思考。

vuex state,前端data view,前端DOM actions,使用者操作,引起data變化從而導致DOM變化。

多個元件(檢視)共享狀態:通俗來講,就是多個元件間會通訊時,導致從後端拿來的資料發生變化,當元件較多時,如果兄弟元件間的通訊都依賴父元件進行通訊,會導致元件間的耦合非常高,從而導致專案邏輯混亂,難以維護。

多個元件(檢視)依賴於同一狀態。 來自不同檢視的行為需要變更同一狀態。

全域性單例模式管理,把元件的共享狀態抽取出來 不管在元件樹的哪個位置,任何元件都能獲取狀態或者觸發行為!

實踐出真知:

1.state存放在index.js中,建立的Store例項getter,mutations,actions等,可以分離出來
2.getters存放在getter.js中,資料流為state→getter→元件,getter相當於一個資料獲取過濾器,從倉庫拿特定資料到元件,相當於對computed的集中處理。
3.mutations存放在mutations.js中,資料流為元件→mutations→state,mutations相當於一個資料提交發射器,從元件提交資料到倉庫
4.actions存放在actions.js中,資料流為元件→actions→mutations→state,非同步操作的主要場所。
5.modules是開發大型應用時需要用到的,每個module都有單獨的states,getters,actions以及mutation,有一股nodejs模組的味道。
複製程式碼

vuex三原則:

1.唯一資料來源
2.保持狀態只讀
3.資料改變只能通過純函式完成 更改 Vuex 的 store 中的狀態的唯一方法是提交 mutation。
複製程式碼

一定要注意mutation和action的區別!

mutation只變更本地的狀態,也就是說,直接只去修改store中的資料。 action包含非同步操作,直接呼叫api,通過api的資料,再提交mutation。

可以說,action只比mutation多了一個非同步呼叫api的操作,因為呼叫api後,一般有2種返回結果,succes或者error,或者是promise的多種狀態,根據不同的。

最近在學習redux,元件dispatch一個action到store,相當於傳送一個http請求,然後store做出響應,返回一個response給元件。和vuex大致類似,唯一有區別的是,vuex還需要引入react-redux,引入Provider和connect連線元件和store。

6.統計字串中單詞出現次數

" hi how are you i am fine thank you youtube am am ",統計"you"出現的次數。

function wordCount(str,word){
  var str = str || "";
  var word = word || "";
  var strArr = str.split(" ");
  var count = 0;
  for(var i=0;i<strArr.length;i++){
      if(word===strArr[i]){
          count++
      }
  }
  return count;
}
wordCount("hi how are you i am fine thank you youtube am am","you");
複製程式碼

如果字串沒有空格怎麼辦?

function wordCount(str,word){
  var str = str || "";
  var word = word || "";
  var count = 0;
  var index = str.indexOf(word);
  while(index!==-1){
      count++;
      str = str.substr(index+word.length);
      index = str.indexOf(word)
  }
  return count;
}
wordCount("hihowareyouiamfinethankyouyoutubeamam","you");
複製程式碼

如果不用js內建字串函式,自己用每個字元對比呢?

function wordCount(str,word){
  var num = 0;
  var str = str+"  " || "";
  var word = word || "";
  var strArr = str.split("");
  var wordArr = word.split("");
  var count = 0;
  function compare(arr1,a,arr2,b){
        if(b+a<arr2.length){
          if(arr1[a]===arr2[b+a]){
            num++;
            return compare(arr1,a+1,arr2,b+1)
          }
          if(num===arr1.length){
            count++
            num = 0;
          }
        }
  }
  for(var i=0;i<strArr.length;i++){
      for(var j=0;j<wordArr.length;j++){
        if(wordArr[wordArr.length-1]===strArr[i+wordArr.length-1]){
          compare(wordArr,0,strArr,i+0)
        }
      }
  }
  return count;
}
wordCount("hihowareyouiamfinethankyouyoutubeamam","am");
複製程式碼

可以更加高效一些嗎?

function wordCount (str,word) {

  var str = str+"  " || "";
  var word = word || "";
  var strArr = str.split("");
  var wordArr = word.split("");
  var wordArrLen = wordArr.length;
  var count = 0;
  var num = 0;

  function compare (arr1,a,arr2,b) {
        if(b+a<arr2.length){
          if(arr1[a]===arr2[b+a]){
            num++;
            return compare(arr1,a+1,arr2,b+1)
          }
          if(num===arr1.length){
            count++;
            num = 0;
          }
        }
  }

  var j = 0;
  while(j<wordArrLen){
      var i = 0;
      while(i<strArr.length){
          if(wordArr[wordArrLen -1]===strArr[i+wordArrLen -1]){
            compare(wordArr,0,strArr,i+0);
          }
          i++;
      }
      j++;
  }
  return count;
}

wordCount("hihowareyouiamfinethankyouyoutubeamam","a");

//1.調整最高層級遍歷陣列,從37的2次方降到3的2次方,從1369降到9
//2.合併控制變數和控制條件,使用while替代for,去除JS引擎查詢i,j是否存在的消耗,會稍微降低程式碼可讀性
//3.對重複引用的wordArr.length,賦值給區域性變數wordArrLen,在這裡,Array.prototype.length的查詢次數從3次降低到1次
複製程式碼

面試2 - offer2 莫愁前路無知己,天下誰人不識君

我本來就不想再繼續找了,但是今天朋友給我一個建議,跳槽前多投幾家,說不定可以找到更好的,然後我就想到 V 站上看到好多求職貼,所以就索性再試試看有沒有更好的坑。

簡歷: 前端開發-簡書-趁你還年輕233

簡書 : 趁你還年輕233

github: FrankKai

segmentFault:趁你還年輕

座標:杭州

然後我在V站上發了貼,很有緣分,一個V友加了我的好友,約好時間進行了一次電話面試,後面瞭解好公司技術氛圍和商議好薪資待遇後,成功拿到了offer,薪資待遇和第一家差不多,這家是浙大系的公司。

我和麵試官很聊得來,最後甚至都聊到了吃雞,由於電話面試太愉快了,所以這次電話面試到現在我僅記得一道跨域題了。第三家公司面試官也很贊,天氣寒冷,友情提醒我注意路上冰凍,不過由於第三家面試在明天晚上才進行,所以我把第三家的BOSS直聘題也總結在一起了:一些特別棒的面試題[1]

貼一下面試題和我的回答。

1.平時有遇到過跨域問題嗎?
2.下面這段程式碼最終輸出什麼?
3.["1","2","3"].map(parseInt)返回的是什麼?
4.下面程式碼中“入庫”的顏色是?
複製程式碼

1.平時有遇到過跨域問題嗎?

說到跨域問題,就一定要說到同源,什麼是同源,相同協議,相同域名,相同埠,即為同源。

不同源之間的通訊就會有跨域問題,一般來說是客戶端訪問伺服器,伺服器上去配置跨域。 我遇到的跨域問題都是後端去配置一下就可以解決的,比如我前端在用vue的官方推薦非同步請求庫axios,去請求後端的koa開啟的後端服務時,就會遇到跨域的問題,例如koa使用依賴koa-cors就可以,具體的話,就是Access-Control-Allow-Origin: 源名,可以為*或者是特殊的源。或者是傳統的maven或者nginx上,也可以很方便的配置跨域。

JSONP有用過嗎?JSONP沒用過,但是原理貌似是通過js載入一個script DOM標籤進來,然後在新的script的src中引入想要執行的程式碼。 其實跨域問題在後端中也有類似的,只不過是叫做程式間通訊,有IPC,RPC等等方式進行程式間通訊。

2.下面這段程式碼最終輸出什麼?

let O = function(name){
 this.name = name || 'world';
};
O.prototype.hello = function(){
 return function(){
  console.log('hello ' + this.name);
 };
};
let o = new O;
let hello = o.hello();
hello();
複製程式碼

年輕的我的答案是:hello world。

答案顯然是不對的,因為這是一道陷阱題,陷阱就在於O.prototype.hello呼叫後,return的是一個函式,這麼做的話,在執行新例項o的hello方法是,this其實已經變成了window。

那麼答案是hello undefined嗎?

年輕的你又錯了,並不是。

而是 hello 。

請注意,是hello ,而不是hello undefined,而是空字串

原因就在於window.name是事先是有定義的,而且其值為空。

不信的話你可以在控制檯列印window.name,返回的是"",你再列印window.yourname試試看,比如window.frank,返回的就是undefined了。

感謝@ygh1的提醒,列印結果和執行環境也是有關的,因為node中全域性是global,browser中全域性是window。

剛在node裡跑了下有陷阱的題目,列印出來確實是hello undefined,因為node中的global物件沒有初始的name屬性。

所以最正確的答案應該是:

node環境:hello undefined
browser環境:hello _____(非零寬空字元)
複製程式碼

而我在工作中,遇到的更多的常見的像上面一樣的工廠函式式的寫法是這樣的。

let O = function(name){
 this.name = name || 'world';
};
O.prototype.hello = function(){
  console.log('hello ' + this.name);
};
let o = new O("frank");
let hello = o.hello("frank");
複製程式碼

列印結果為:hello frank。

如果不傳入frank的話,列印出的是預設值hello world。

3.["1","2","3"].map(parseInt)返回的是什麼?

A. [1,2,3] B.["1","2","3"] C.[1,1,1] D.其他

這特麼又是一道陷阱題,還好我之前在看MDN的map函式時,看到過這個陷阱。

正確答案是D:其他。

其他的具體值為多少?[1,NaN,NaN]。

不敢相信吧,為什麼不是可愛的[1,2,3]呢?

因為map的callback有3個引數,currentValue,index和array,parseInt有2個引數,string和radix(進位制),只傳入parseInt到map中的話,會自動忽略第三個引數array,但是不會忽略index,所以就會把0,1,2作為第二個引數傳給parseInt。

如果還不明白的話,我們把["1","2","3"].map(parseInt)的每一步都拆開來。

parseInt("1",0) 此時將字元"1"轉換為O進位制數,由於0進位制數不存在,所以返回Number型別的1。
parseInt("2",1) 此時將字元"2"轉換為1進位制數,由於超出進位制數1,所以返回NaN。
parseInt("3",2) 此時將字元"3"轉換為2進位制數,由於超出進位制數2,所以返回NaN。
複製程式碼

至此,真相大白。 那麼常用的非陷阱式map寫法是怎樣的呢?

像這樣:["1","2","3"].map(x=>parseInt(x))

傳一個完整的函式進去,有形參,有callback,這樣就不會造成因為引數傳入錯誤而造成結果錯誤了,最後返回一個漂漂亮亮的經由純函式處理後的新陣列回來。

其實這裡如果再深入的話,可以再考察純函式是什麼?

純函式其實就是一個不改變輸入,但是可以藉助輸入,產生一個以輸入為原材料,經過加工處理後,輸出一個全新的輸出的函式,關鍵在於不改變輸入,純函式是編寫redux的reducer必須具備的技能點。

剛才公司的大牛過來,說他從來不用parseInt,他用加號,+"1" 返回1,+"2"返回2。大牛果然大牛,黑科技是真的多。

4.下面程式碼中“入庫”的顏色是?

<ul class="list" id="list">
 <li class="favorite">
  <span>出庫</span>
 </li>
 <li class="favorite">
  <span class="highlight">入庫</span>
 </li>
</ul>
<style>
#list .favorite:not(#list) .highlight{
 color: red;
}
#list .highlight:nth-of-type(1):nth-last-of-type(1){
 color: blue;
}
</style>
複製程式碼

A. red B.blue C.black

我的答案是:我猜一下,可能是A,因為A的權重是最大的,偽類選擇器的權值應該比較小吧。

面試官發來一個?,明天可以來公司面談嗎?已經約好明天面試。

這道題的解答到此為止,因為我是真的真的對CSS不感興趣,各位看官老爺請原諒我。

面試-3 未通過

1.說下下面兩種font-size單位的異同?

em rem

二者的為了保證使用者修改字型大小時,保持垂直方向上的字型大小一致。與px不同,二者都是字型計算大小單位,也就是說,需要通過計算得出其大小,轉換成px,微信小程式的rpx也是這樣,最後還是轉換成了px,可能是借鑑了rem的思想吧。

但是em相對於繼承來的父元素,rem相對於根元素。聽大牛說,rem在國內使用比較多,可能侍使用習慣問題。我自己也覺得rem使用起來更簡單,為根元素的font-size賦一個初始值,再配合css的媒體查詢,可以動態的改變這個全域性的單位,可以說是牽一髮而動全身,使用起來非常方便,而em的可閱讀性就很差了,有的時候為了算字型大小,需要一級一級找上去,非常不直觀。

現代的常用的瀏覽器,1rem等於16px,但是可以通過html{font-size:percentage/num }來控制。

舉2個 rem和em例子對比下。

html簡寫結構:

<html>
    <body>
        <div></div>
    </body>
</html>
複製程式碼

rem 例子:

html { font-size:62.5%; }  /* =10px */
body { font-size: 2.0rem; } /* =20px */
div   { font-size: 1.0rem; } /* =10px */
複製程式碼

em 例子:

html { font-size:62.5%; }  /* =10px */
body { font-size: 2.0em; } /* =20px */
div   { font-size: 1.0em; } /* =20px */
複製程式碼

MDN的font-size章節給出了em和rem的非常好的解釋,英文原版非常直觀,我這裡再貼一下:

em

Represents the calculated font-size of the element. If used on the font-size property itself, it represents the inherited font-size of the element.

rem

Represents the font-size of the root element (typically ). When used within the root element font-size, it represents its initial value (a common browser default is 16px, but user-defined preferences may modify this).

其實em和rem與MVVM框架的元件間通訊有些類似,都有逐級繼承和全域性影響的概念。em是逐級傳遞的,也就是繼承,框架中用props和事件訂閱釋出的方式也是這樣,爺,父,孫的傳遞都是要一級一級去傳遞的,爺爺想直接傳授點技能給孫子必須先傳授給父親,爺爺→父親→孫子;而rem就和框架中的狀態管理庫很像,例如vuex和redux,抽取出一個全域性的狀態樹,不用一級一級的去很複雜的去繼承,爺爺想教教孫子,直接就可以傳授給孫子,爺爺→孫子

2.只用一個div 實現定時紅綠燈

default

<!DOCTYPE html>
<html lang="zh-cn">
    <head>
        <meta charset="utf-8" />
        <title>僅用一個DIV實現紅綠燈</title>
        <style>
        	@keyframes light{
        		0%{
        			background-color: green;
        			left: 0;
        		}
        		33.3%{
        			background-color: green;
        			left: 0;
        		}
        		33.4%{
        			background-color: yellow;
        			left: 200px;
        		}
        		66.6%{
        			background-color: yellow;
        			left: 200px;
        		}
        		66.7%{
        			background-color: red;
        			left: 400px;
        		}
        		99.9%{
	       			background-color: red;
        			left: 400px;
        		}
        	}
        	.traffic-light{
        		position: relative;
        		width: 200px;
        		height: 200px;
        		border-radius: 50%;
        		animation: light 3s ease-in-out 0s infinite;
        	}
        	.container{
        		width: 600px;
        		border:10px solid #000;
        		border-radius: 20% 20%;
        	}
        </style>
    </head>
    <body>
    	<div class="container">
    		<div class="traffic-light"></div>
    	</div>
    </body>
</html>
複製程式碼

面試-4 未通過

由於種種原因,開始了新一輪的面試,這一輪面試可謂收穫頗豐。 與各種各樣的面試官交流下來,除了收穫到一些疏漏的知識點外,發現面試其實非常考驗面試官的水平。揣摩出一些如何成為一名合格的前端面試官方法。

以及很重要的老哥的經驗:進大廠前必須要做的準備,提前一個月刷題。

1.setTimeout與函式呼叫棧

console.log(1);
setTimeout(function(){
    console.log(2);
},0);
console.log(3);
複製程式碼

輸出:1 3 2 原因:Call Stack會最後呼叫setTimeout的callback,setTimeout中的callback是一個非同步函式。 函式呼叫棧的部分可以參考這裡:blog.risingstack.com/node-js-at-…

2.function foo 與 var foo的提升優先順序

console.log(typeof foo);
var foo = "foo";
function foo(){}
複製程式碼

輸出:function

console.log(typeof foo);
function foo(){}
var foo = "foo";
複製程式碼

輸出:function

function優先順序比var高,無論在其前後,都會覆蓋掉同名的var宣告。

3.let 塊作用域 與 setTimeout

for(let i=0;i<6;i++){
    setTimeout(function(){
        console.log(i)
    },0)
}
console.log(i)
複製程式碼

輸出:

0
Uncaught ReferenceError: i is not defined
1
2
3
4
5
複製程式碼
  • Uncaught ReferenceError: i is not defined 由此可見for語句的塊作用域,不僅僅在花括號中生效,在圓括號中也生效。
  • 輸出0 1 2 3 4 5 的原因 setTimeout的callback是非同步函式,for迴圈實質上是在做非同步迴圈佇列,setTimeout的callback會被呼叫5次,由於let會為每次的i分配獨立的地址空間,因此每一次傳不同的值進去。

為什麼在debug的過程中,列印順序是混亂的? (等把規範的timers章節翻譯完,再來解決) breakpoint打在console.log(i)上。

Uncaught ReferenceError: i is not defined
0 
2
5
4
3
1
複製程式碼

如果將let替換成var呢?

for(var i=0;i<6;i++){
    setTimeout(function(){
        console.log(i)
    },0)
}
console.log(i)
複製程式碼

輸出: 6個6 原因:

  • 列印的是window.i 每個傳入的i指向相同的i,傳入時依次window.i的值為1,2,3,4,5,6,但是都是同一個引用,當函式呼叫棧開始執行setTimeout的callback時,window.i已經變為了6
  • var 不會限制塊作用域 不會分配6個獨立的地址空間給setTimeout的callback

4.為什麼Object.toString.call([1,2,3])返回[object Array]?[].toString()可以返回[object Array]嗎?

若想回答這個問題,需要深入理解Object.prototype.toString.call()

難道真的像自己理解的那樣,是通過call將[1,2,3]作為Object.toString的實參傳遞了進去嗎?不是。 直接Object.toString([1,2,3])不能實現同樣的功能嗎?不能。 而實際上也有Array.proto.toString()這種形式,所以是可以直接呼叫arr.toString()的,這樣能檢測出嗎?不行。

那到底是什麼原因? 先來肝一個表格。


資料型別 例子 return
字串 "foo".toString() "foo"
數字 1.toString() Uncaught SyntaxError: Invalid or unexpected token
布林值 false.toString() "false"
undefined undefined.toString() Uncaught TypeError: Cannot read property 'toString' of undefined
null null.toString() Uncaught TypeError: Cannot read property 'toString' of null
String String.toString() "function String() { [native code] }"
Number Number.toString() "function Number() { [native code] }"
Boolean Boolean.toString() "function Boolean() { [native code] }"
Array Array.toString() "function Array() { [native code] }"
Function Function.toString() "function Function() { [native code] }"
Date Date.toString() "function Date() { [native code] }"
RegExp RegExp.toString() "function RegExp() { [native code] }"
Error Error.toString() "function Error() { [native code] }"
Promise Promise.toString() "function Promise() { [native code] }"
Obejct Object.toString() "function Object() { [native code] }"
Math Math.toString() "[object Math]"

為什麼會出現下面的情況?

Object.toString.call(Array)//"function Array() { [native code] }"
Object.prototype.toString.call(Array)//"[object Function]"
複製程式碼

答案在這裡!

Object.toString()//"function Object() { [native code] }"
Object.prototype.toString()//"[object Object]"
複製程式碼

Object物件和它的原型鏈上各自有一個toString()方法,第一個返回的是一個函式,第二個返回的是值型別。

既然知道了不同,現在我們再來分析下Object.prototype.toString.call(Array)//"[object Function]"。 Array物件本身返回一個建構函式,Array//ƒ Array() { [native code] },而Object.prototype.toString()返回的是//"[object Type]"的形式,通過call將Array的this上下文切換到Object,從而呼叫了Object.prototype.toString(),因此返回[object Function]

需要注意的是:Math.toString()直接返回"[object Math]"。

實際開發中,我們用到最多的可能是:Object.prototype.toString.call([1,2,3])//"[object Array]"這種。

總結:

  • 一般情況下,js中物件的toString(),返回字串,內容與函式宣告語法有關,例如[1,2,3].toString()//"1,2,3"
  • 大多數都返回函式的完整原始碼,Array.toString()//"function Array() { [native code] }"
  • 內建函式往往返回一個類似"[native code]"的函式體,需要配合call方法,比如Object.prototype.toString.call([1,2,3])//"[object Array]"

那麼不可以直接Array.prototype.toString.call([1,3,4])嗎? 不行! 因為Array,Function,Date雖然是基於Object進行建立的,但是他們繼承的是Object.toString(),而不是Object.prototype.toString()。 再加深一遍印象:

Object.toString()//"function Object() { [native code] }"
Object.prototype.toString()//"[object Object]"
複製程式碼

所以這就是必須用Object.prototype.toString()去檢測型別的原因。

至於Object.prototype.toString()內部是怎麼實現的,等到時機成熟再去深入。

5.綜合考察bind,call和apply的面試題

var obj = {
    a: 1,
    name: 'world',
    objSayName: function (fn) {
    	fn();
    }
}
function sayName () {
	return console.log(this.name);
}
obj.objSayName(sayName);
// 輸出:undefined
複製程式碼

為什麼? 在obj的objSayName內部,沒有修改this指向到當前呼叫物件。

題目一:物件內部方法,呼叫全域性的函式 適用:多個物件(區域性方法)複用同一全域性函式 精簡:區域性(方法)複用全域性函式 方法:修改this指向,通過Function.prototype.bind()去顯式修改this指向到當前呼叫物件。 原因:Calling f.bind(someObject) creates a new function with the same body and scope as f, but where this occurs in the original function,in the new function it is permanently bound to the first argument of bind, regardless of how the function is being used.bind only works once!

var obj = {
    name: '1',
    objSayName: function (f) {
      var g = f.bind(this);
	  console.log(g());
    }
};
function sayName(){
    return this.name;
}
obj.objSayName(sayName);

複製程式碼

輸出:'1'

擴充: 題目二:如果全域性方法想輸出物件的區域性屬性,該怎麼辦? 適用:同一全域性函式輸出多個物件(內部變數) 精簡:全域性函式輸出區域性(變數) 方法:使用apply或者call修改this指向到被呼叫物件 原因:An object can be passed as the first argument to call or apply and this will be bound to it.

var obj = {
    name: '1',
    say: function (fn) {
        fn();
    }
};
function sayName(){
    return this.name;
}
sayName.apply(obj);
sayName.call(obj);
// 輸出:'1'
複製程式碼

參考:developer.mozilla.org/en-US/docs/…

面試-5 offer 3 整個江湖都任我闖,我的生命像一首歌

現東家的面試題,是我最欣賞的一套面試題,在這裡已經工作了接近1年時間,成長許多。

題目主要分為4個部分:

  • HTML
  • CSS
  • JS
  • 程式碼

答案未驗證準確性,有待驗證並更新。 我將按照“1 year before”和“today”的角度去進行解答,也是對自己成長的一個記錄。

HTML

1.請描述cookies, sessionStorage和localStorage的區別

1 year before: cookies,需要與後端協作,一種認證方式,expires失效時間 sessionStorage,當前會話有效 localStorage,本地快取,expires失效時間 today:

cookies
  • 一個HTTP cookie(web cookiebrowser cookie)是伺服器傳送給使用者web瀏覽器的一小段資料段。
  • 瀏覽器可以把它存在自己本地,並且將它在下一個請求中傳送給它的資料來源伺服器。
  • 典型的應用場景就是去判斷兩個請求是否來自同一個伺服器----來自同一個登入使用者,它是對無狀態的HTTP協議的一種有狀態記憶。
sessionStorage
  • key/value的方式儲存,會話儲存
  • 為每一個給定的origin維護一個分離的storage區域,在頁面會話期間可用
localStorage
  • key/value的方式儲存,持久化儲存
  • 瀏覽器關閉後重新開啟,localStorage的鍵值對仍然存在

2.請解釋<script><script async><script defer>區別

1 year before: 常見的同步;非同步;延遲載入。

today:

  • <script async>控制瀏覽器同非同步載入指令碼。若設定async為false,那麼瀏覽器在HTML解析期間同步載入指令碼。一般來說通過程式碼document.creatElement()插入指令碼是非同步的,設定async為false可以將其控制為同步。
  • <script defer>defer屬性用來控制script是否在HTML完成解析之後再執行,但是會在DOMContentLoad燃燒之前完成。

擴充: 1.什麼是DOMContentLoaded事件? DOMContentLoaded事件,會在初始化HTML document完成載入和解析觸發,無需等待stylesheets,images,以及字frame結束載入。Load事件和DOMContentLoaded事件很類似,經常有人混淆2個事件。 2.什麼是Load事件? load事件,會在完成一次完全載入之後再觸發。 3.如何解決同步js阻塞DOM渲染的問題,最快速度渲染DOM? 非同步化js優化stylesheets載入。 4.如何檢查document是否完成載入? document.readyState

  • loading 載入中。
  • interactive document完成載入,document已經被解析但是子資源例如images、stylesheets和frame賑災載入。
  • complete document以及子資源都已載入完成,此時會觸發load事件。

所以一次載入文件資源的狀態有3個,loading``interactivecomplete

3.為什麼通常推薦將CSS 放置在<head></head>之間,而將js<script></script>放置在</body>之前?

1 year before: 防止阻塞瀏覽器渲染,先執行CSSOM,再繪製DOM,再操作DOM。主執行緒單執行緒渲染UI。

today 從技術實現的角度講,先載入stylesheets並且繪製,DOM渲染結束後再載入並執行可能涉及DOM操作的js檔案,避免js的執行導致渲染阻塞。 從使用者體驗的角度講,優先載入樣式表減少使用者面對空白網頁的時間,提升使用者體驗。

CSS

1.CSS中類(classes)和ID的區別

1 year before ①ID優先順序高 ②ID唯一標識,不能重複

today ①語法不同,#idname, .classname ②影響元素不同,#idname作用單個元素,.classname作用所有元素 ③選擇器權重不同,#idname權值高於.classname

2.有哪些隱藏DOM的方法

1 year before

  • display: none
  • position: top right bottom left
  • transform
  • z-index
  • 移除DOM節點(JS)

today [譯]如何隱藏DOM元素?

3.請解釋*{ box-sizing: border-box }的作用,並且說明使用它有什麼好處?

1 year before 怪異盒模型。 計算方便,width包含了border,響應式百分比佈局。

today box-sizing的值有2種,一個是content-box,一個是border-box,content-box僅僅包含content。 border-box的width會將content, padding和border包含在內,例如width:100%指的是包含了content,padding和border的寬度,佈局時更好控制。

例如子元素繼承了父元素的width:100%,此時設定了子元素padding,border,若子元素的box-sizing是content-box,會導致溢位,而border-box的話,width:100%會很舒服地包含了padding和border。

因為這樣的應用場景很多,所以索性就為所有標籤都設定成border-box,有特殊情況的再手動設定成content-box。

image

有一篇牆推IE怪異盒模型的文章:把所有元素的box-sizing都設定成border-box吧!

4.請問在確定樣式的過程中優先順序是如何決定的(請舉例)?如何使用此係統?

1 year before

  • style屬性 <div style=""></div>
  • id
  • 標籤名
  • class
  • 偽類

每個選擇器都有權值,權重為和,#foo>.class::before 三者和為權重,權值id>標籤>class>偽類。

today 優先順序由高到低

  • !important 不建議的實踐
  • 行內樣式 <span style=""></span>
  • ID選擇器 #foo
  • 類選擇器.foo,屬性選擇器[type="radio"],偽類:hover
  • 型別選擇器p,span,偽元素::before

一個很優秀的說明css選擇器優先順序的圖:specifishity.com/

image

通用選擇器(*),組合符(+,>,〜,'',||)和否定偽類(:not())對權重沒有影響。

5.請問為何要使用translate()而非absolute positioning,或反之的理由?為什麼?

1 year before absolute

  • 從正常文件流移除
  • 不佔據頁面佈局空間
  • 開闢新的stacking context
  • 影響其他佈局元素
  • 消除邊緣重疊

減少了計算,translate()不影響其他元素,效能更好,GPU計算次數更少

today 在我的這篇博問中有答案:CSS3動畫卡頓效能優化解決方案

translate()涉及到的是合成器執行緒,與主執行緒是相互獨立的,所以會比較快。 而absolute positioning涉及到的是主執行緒,會導致主執行緒負責的佈局重繪和js執行,所以會比較慢。

JS

1..call和.apply的區別是什麼?

1 year before

  • 傳入引數方式不同,但都是動態修改this上下文
  • call 會有函式執行棧的操作
  • apply僅僅將arguments傳到新的類中

today 關於這個問題曾經產出2篇部落格: 從規範去看Function.prototype.apply到底是怎麼工作的? 從規範去看Function.prototype.call到底是怎麼工作的? call是索取,apply是付出。 從call和apply的字面意思就可以看出,call呼叫,apply應用,呼叫函式,應用引數。 call和apply的主要區別在於,call僅僅切換this上下文到其他類,從而呼叫自己不存在的方法;而apply主要是為了將其他型別的引數傳遞到自己內部,再呼叫自己的方法。

假設有foo,bar。 foo.call(bar) bar呼叫foo的方法,例項Object.toString([1,2,3])->"[object Array]",陣列例項呼叫了Object類的toString方法。 foo.apply(null, bar) foo的方法應用bar的引數,Math.max.apply(null, [1,2,3])->3,Math的max方法應用了[1,2,3]中的每個引數。

2.請解釋JSONP的工作原理,以及它為什麼不是真正的Ajax?

1 year before JSONP 原理:非同步插入一個<script></script>,會有XSS問題 原因:沒有呼叫XMR物件 today 一種比較古老的不安全的跨域請求訪問方式,沒有呼叫XMR物件,伺服器允許瀏覽器在query parameter中傳遞瀏覽器內定義的函式,而這個函式是有概率被XSS攻擊改寫的。

來自StackOverflow高票答案:stackoverflow.com/questions/2…

JSONP不是一個很複雜的問題。 假設我們在example.com域名下,此時我們想給example.net域名傳送一個請求。為了請求成功,需要跨越域名邊界,這對於瀏覽器來說是禁忌。

繞開這個限制的一種方式是<script>標籤。當你使用script標籤的時候,域名限制會被忽略,但是對結果不能做任何處理,指令碼是被評估過的。

開始進入JSONP。當你傳送一個請求到支援JSONP的伺服器,你會傳一些特殊的關於你的頁面的資料給伺服器。這種方式下,伺服器會以你的瀏覽器處理起來方便的方式包裝響應。

例如,伺服器需要一個叫做callback的引數去開啟JSONP功能。然後你的請求會想下面這樣: http://www.example.net/sample.aspx?callback=mycallback

沒有JSONP的情況下,這可以返回一些基本的JS物件,例如: {foo: 'bar'}

然而,在支援JSONP的情況下,伺服器接收到callback引數,它包裹結果的方式是不一樣的,會返回下面這樣的資料: mycallback({foo: 'bar'});

就如你縮減,他會呼叫瀏覽器端的方法。所以,在你的頁面上callback的定義如下:

mycallback = function(data){
  alert(data.foo);
};
複製程式碼

現在的話,在指令碼載入成功後,它會被評估,然後函式執行。cross-domain請求成功!

所以JSONP存在一個很嚴重的問題:你失去了對請求的控制。例如,無法得知錯誤程式碼的返回。可以使用定時器去監控請求,但是這不是很好。JSONRequest是一個非常好的跨域指令碼執行的方式,安全,並且獲得更多對請求的控制。

2015年,CORS是一個與JSONRequest可以抗衡的方案。JSONP在老式瀏覽器下仍舊有用,但是不安全。 CORS是做跨域請求訪問的更安全、更高效的一種方式。

3.用過javascript模板系統嗎?都使用過哪些庫?

1 year before jade vue angular react { ... }或者{{ ... }}

today: 一些好用的模板引擎庫

常用的還是前端框架自帶的以及pug,由於我只對vue.js的比較熟悉所以就沒有羅列react和angular的demo。

4.== 和 === 有什麼不同?

1 year before 這個問題寫過部落格,看過規範。 ①===是==的子集 ②==有型別轉換 ③規範內實現機制不同 today 你真的理解==和===的區別嗎?

5.請解釋Javascript的同源策略(same-origin policy)

1 year before 瀏覽器安全機制

  • same-origin:相同協議,相同host,相同埠
  • 跨域問題:CORS 可以後端設定允許 cross-origin-access資訊

today 如何理解same-origin policy?

6.你使用過Promises及其polyfills嗎?請寫出Promise的基本用法(ES6)

1 year before axios

  • axios get() post()
  • 前端請求、響應攔截器

today

  • Promise.all() 只有當所有的非同步Promise執行完為resolved狀態時,返回一個Promise
  • Promise.prototype.catch() 捕捉Promise執行過程中捕捉到的異常
  • Promise.prototype.finally() 是catch和then的並集,常用於在catch和then中都執行的程式碼,避免重複
  • Promise.prototype.then() Promise執行完畢,進行下一步
  • Promise.race() Promise競爭,返回Promise集合中最先resolve或者reject出來的Promise;常用於時間控制、中止Promise 可以參考我發的一篇帖子:什麼情況下會用 Promise.race()?
  • Promise.reject() Promise類捕捉異常
  • Promise.resolve() Promise類解析資料

7.使用Promises而非回撥(callbacks)優缺點是什麼?

1 year before 解決了callback hell問題。

today 以一個圖片資源載入繪製canvas。

class CanvasImage {
  constructor(url, width, height) {
    this.width = width || 500;
    this.height = height || 500;
    this.url = url || '';

    this.element = new Image(this.width, this.height);
  }
  get imageElement() {
    return this.element;
  }
}
複製程式碼

callback hell方式

source.onload = function() {
  const target = new CanvasImage(url, 80, 80).imageElement;
  target.onload = function() {
    const main = new MainCanvas(source, target, 'right').canvasElement;
    context.drawImage(main, 0, 0, 500, 500);
  };
};
複製程式碼

Promise方式

const sourcePromise = new Promise((resolve) => {
  setTimeout(() => {
    const target = new CanvasImage(url, 80, 80).imageElement;
    resolve(target);
  }, 0);
});
source.onload = function() {
  sourcePromise.then((target) => {
    const main = new MainCanvas(source, target, 'right').canvasElement;
    context.drawImage(main, 0, 0, 500, 500);
  });
};
複製程式碼

async/await方式

 function sourceCanvasImage() {
  return new Promise((resolve) => {
    setTimeout(() => {
      const target = new CanvasImage(url, 80, 80).imageElement;
      resolve(target);
    }, 0);
  });
}

async function mergeCanvas() {
  const targetElement = await sourceCanvasImage();
  const main = new MainCanvas(source, targetElement, 'right').canvasElement;
  context.drawImage(main, 0, 0, 500, 500);
}

source.onload = function() {
  mergeCanvas();
};
複製程式碼

8.什麼是事件迴圈(event loop)?

1 year before

  • 非同步原理
  • 事件訂閱、事件釋出
  • 非同步非阻塞式事件

today 官方的解釋更加權威:nodejs.org/de/docs/gui…

什麼是Event Loop?event loop允許Node.js執行非阻塞的I/O操作-----儘管JS是單執行緒的-----它儘可能地通過把操作解除安裝到系統核心(kernel)。

因為大多數現代的核心(kernel)是多執行緒的,它們可以在後臺處理多個操作。當其中之一完成後,kernel會告訴Node.js一個適當的callback可以被新增到poll queue(輪詢佇列)中並且執行。

更多原理上的內容,可以參考:[譯]Node.js Event Loop,Timers和 process.nextTick()

event loop,原理細節包括以下內容

  • call stack
  • (macro)task queue
  • microTask queue
  • background thread

其中前三部分屬於main thread,可以閱讀node原始碼一探究竟。最後的background thread屬於libuv的部分,可以去深入libuv原始碼(這是一個專門處理非同步的c語言庫)理解其實現機制。

但是閱讀原始碼需要非常好的基礎。這裡推薦一篇囊括了以上知識點的非常走心的文章:blog.risingstack.com/node-js-at-…

程式碼

1.foo的值是什麼?

var foo = 10 + '20';

1 year before: 30

today: "1020"

2.如何實現以下函式?

add(2, 5);// 7
add(2)(5);// 7
複製程式碼

1 year before 通過arguments類陣列物件。

function foo(){
    var sum = 0;
    for(var i = 0; i<arguments.length; i++){
        sum += arguments[i]
    }
    if(arguments.length>=2){
        return sum;
    }else{
        return foo;
    }
}
複製程式碼

add(2)(5)執行失敗。

today

const add = (a, b) => {
    const result =
      a && b
        ? a + b
        : function(b) {
            return a + b;
          };
    return result;
 };
複製程式碼

3.下面兩個alert的結果是什麼?

var foo = "Hello";
(function() {
    var bar = " world";
    alert(foo + bar);
})();
alert(foo + bar);
複製程式碼

1 year before "undefined World" "Hello undefined"

today "Hello world" // 立即執行函式作用域可以訪問全域性變數 Uncaught ReferenceError: bar is not defined // 全域性作用域不可以訪問區域性變數

4.下面程式碼的輸出是什麼?

console.log('one');
setTimeout(function() {
    console.log('two');
}, 0);
console.log('three');
複製程式碼

1 year before one three two

today one three two

原因:優先執行函式呼叫棧中任務,setTimeout排入巨集任務佇列延後執行。 event loop包括call stack,(巨集)任務佇列,微任務佇列和background thread,而call stack中的普通程式碼優先執行,setTimeout會經由background thread進入巨集任務佇列,巨集任務佇列間隙執行微任務佇列。

image

5.下面程式碼的輸出是什麼?

function getResult(value, time){
    return new Promise((resolve)=>{
        setTimeout(()=>{
            console.log(value);
            resolve();
        }, time);
    });
}
(async () => {
    const a = getResult(1, 300);
    const b = getResult(2, 200);

    await a;
    await b;
})();
(async () => {
    await getResult(1, 300);
    await getResult(2, 200);
})();
複製程式碼

1 year before 1 2

today 2 1 1 2

這是一道考察async的題目:如何理解async函式?

6.下面的程式碼輸出是什麼?

var obj = {
    a: 1,
    name: 'world',
    objSayName: function(fn) {
         fn();
    }
}
var name = 'hello';
var arr = [1, 2, 3, 4, 5];

function foo(o) {
    var bar = arr || [6, 7, 8];
    var arr = [4, 2, 9];
    var baz = o;
    baz.a = 2;
    console.log(bar, obj.a);
}

function sayName() {
    return console.log(this.name);
}

foo(obj);
obj.objSayName(sayName);
複製程式碼

1 year later [4,2,9] 2 'hello'

today [6,7,8] 2 'hello'

原因: 變數提升。

var bar,arr,baz;
bar = arr || [6, 7, 8];// 此時arr是undefined,因此bar得到了[6, 7, 8]賦值
arr = [4, 2, 9];
複製程式碼

baz獲得了obj的引用,所有修改baz相當於修改obj,所以列印出2.

因為閉包,全域性的sayName函式內的this指向全域性,所以是hello。

我想成為一個很厲害的人,讓這個世界因為有了我而有一點點不一樣,哪怕,只有一點點

貼一下自己理想中的團隊的關注點

1.個人前端技術的成長空間

比如資深前端對初級前端的指導,前端團隊成員間的學習和提升等等 這個最重要,我的目標是2年時間成為基礎紮實,有獨立前端技術見解的開發者

2.公司對員工成長和進步的重視程度

例如新人培訓,技術分享,技術翻譯等等

3.公司加班情況是996還是10105或者是按照專案進度安排的

只要是一幫追求前端極致的人,996是完全OK的

4.員工補貼,福利

例如過節補貼,工會福利

5.績效考核和獎金

例如績效考核,年終獎金

這次年後換工作我希望自己能在一家公司呆至少2年以上,所以這些方面我都需要了解清楚再做決定,需要慎重一些,希望能夠加入一個有趣並且有實力的團隊~

簡歷戳這裡: 前端開發-簡書-趁你還年輕233

最後再說句體題外話,3家公司都在用vue,都問我有沒有用過vue...

That's it !

趁你還年輕

2018年1月31日

於凍成狗的冰雪杭州

如何成為一名合格的面試官?

  • 學會傾聽 無論面試者有多少職場上的不如意想和你傾訴,都要耐心聽他講完,不要打斷他
  • 學會定位 根據面試者的工作經驗和工作場景,對能力進行簡單定位,雖然這偶爾會有所偏頗,但是對於後續提問會有很好的幫助
  • 學會理解 理解面試者想要表達的對知識點的理解,即使他講的很模糊,儘可能捕捉其理解的部分
  • 學會引導 當面試者遇到一道棘手的題目時,如果對方邏輯不清楚,學會化繁為簡,由淺入深去做引導;假設對方對當前的題目遊刃有餘,引導深入原理或者是擴充套件出應用場景
  • 學會挖掘 學會挖掘出面試者身上的閃光點,聰明,執著,這些都是非常好的品質
  • 學會尊重 當面試者急於想得知面試官的評價時,無論你對他是否滿意,都採取"如果有新的進展,我們後續會再通知你"的方式
  • 學會沉默 當面試者想得知你對問題的看法時,不要做太深層次的講解,點到為止,因為有限的面試時間主要目的是為了考察面試者的能力

相關文章