不管是寒冬仍是暖冬,找工做以前都須要作好充足的準備,面試的時候才能作到遊刃有餘。此文是把我最近找工做準備的以及筆試面試中涉及到的手寫題作一個總結。給本身,也給須要的同窗。前端
手寫題是比較好準備的一個環節,大部分公司考察的題也就那麼多,大都不會超出範圍。vue
本文是手寫題系列的第二篇文章。node
觀察者模式是咱們工做中常常能接觸到的一種設計模式。用過 jquery
的應該對這種設計模式都不陌生。eventEmitter
是 node
中的核心,主要方法包括on、emit、off、once
。jquery
class EventEmitter { constructor(){ this.events = {} } on(name,cb){ if(!this.events[name]){ this.events[name] = [cb]; }else{ this.events[name].push(cb) } } emit(name,...arg){ if(this.events[name]){ this.events[name].forEach(fn => { fn.call(this,...arg) }) } } off(name,cb){ if(this.events[name]){ this.events[name] = this.events[name].filter(fn => { return fn != cb }) } } once(name,fn){ var onlyOnce = () => { fn.apply(this,arguments); this.off(name,onlyOnce) } this.on(name,onlyOnce); return this; } }
繼承是一個萬年不變的考點。從ES5到ES6,有許多繼承方法。專門看有關繼承的文章,通常都會從最基礎的prototype
原型鏈繼承 到 借用父類構造函數的call
繼承 到兩者的結合提及。本文只給出終極方法,若是想了解其餘方法的話,能夠自行搜索。面試
// ES5 function Parent(name,age){ this.name = name; this.age = age; } Parent.prototype.say = function(){ console.log('I am' + this.name) } function Child(name, age, sex){ Parent.call(this,name,age); this.sex = sex; } Child.prototype = Object.create(Parent.prototype); Child.prototype.constructor = Child;
// ES6 class Parent { constructor(name,age){ this.name = name; this.age = age; } } class Child extends Parents{ constructor(name,age,sex){ super(name,age); this.sex = sex; // 必須先調用super,才能使用this } }
首先要了解 instanceof
實現的功能,instanceof
運算符用於檢測構造函數的 prototype
屬性是否出如今某個實例對象的原型鏈上。其實考察的也是繼承。vue-router
function myInstanceof(left,right){ var proto = left.__proto__; var protoType = right.prototype; while(true){ if(proto === null){ return false } if(proto == protoType){ return true } proto = proto.__proto__ } }
當咱們new一個對象的時候,具體執行的是什麼?MDN
上給的說明以下:express
JavaScript
對象(即{});this
的上下文 ;this
。以 var child = new Parent()
爲例:json
function newParent(){ var obj = {}; // 首先建立一個對象 obj.__proto__ = Parent.prototype; // 而後將該對象的__proto__屬性指向構造函數的protoType var result = Parent.call(obj) // 執行構造函數的方法,將obj做爲this傳入 return typeof(result) == 'object' ? result : obj }
原題以下:segmentfault
實現一個LazyMan,能夠按照如下方式調用: LazyMan("Hank")輸出: Hi! This is Hank! LazyMan("Hank").sleep(10).eat("dinner")輸出 Hi! This is Hank! //等待10秒.. Wake up after 10 Eat dinner~ LazyMan("Hank").eat("dinner").eat("supper")輸出 Hi This is Hank! Eat dinner~ Eat supper~ LazyMan("Hank").sleepFirst(5).eat("supper")輸出 //等待5秒 Wake up after 5 Hi This is Hank! Eat supper 以此類推。
這道題主要考察的是鏈式調用、任務隊列、流程控制等。關鍵是用手動調用next函數來進行下次事件的調用,相似express
中間件和vue-router
路由的執行過程。設計模式
function _LazyMan(name){ this.nama = name; this.queue = []; this.queue.push(() => { console.log("Hi! This is " + name + "!"); this.next(); }) setTimeout(()=>{ this.next() },0) } _LazyMan.prototype.eat = function(name){ this.queue.push(() =>{ console.log("Eat " + name + "~"); this.next() }) return this; } _LazyMan.prototype.next = function(){ var fn = this.queue.shift(); fn && fn(); } _LazyMan.prototype.sleep = function(time){ this.queue.push(() =>{ setTimeout(() => { console.log("Wake up after " + time + "s!"); this.next() },time * 1000) }) return this; } _LazyMan.prototype.sleepFirst = function(time){ this.queue.unshift(() =>{ setTimeout(() => { console.log("Wake up after " + time + "s!"); this.next() },time * 1000) }) return this; } function LazyMan(name){ return new _LazyMan(name) }
jsonp
的做用是跨域。原理是經過動態插入script
標籤來實現跨域,由於script
腳本不受同源策略的限制。它由兩部分組成:回調函數和數據。舉例:
function handleResponse(response){ alert("You’re at IP address " + response.ip + ", which is in " +response.city + ", " + response.region_name); } var script = document.createElement("script"); script.src = "http://freegeoip.net/json/?callback=handleResponse"; document.body.insertBefore(script,document.body.firstChild); }
根據上面的例子,下面來實現一個通用的JSONP
函數
function jsonp(obj) { const {url,data} = obj; if (!url) return return new Promise((resolve, reject) => { const cbFn = `jsonp_${Date.now()}` data.callback = cbFn const head = document.querySelector('head') const script = document.createElement('script') const src = `${url}?${data2Url(data)}` console.log('scr',src) script.src = src head.appendChild(script) window[cbFn] = function(res) { res ? resolve(res) : reject('error') head.removeChild(script) window[cbFn] = null } }) } function data2Url(data) { return Object.keys(data).reduce((acc, cur) => { acc.push(`${cur}=${data[cur]}`) return acc }, []).join('&') } // jsonp({url:'www.xxx.com',data:{a:1,b:2}})
函數柯里化是把接受多個參數的函數變換成接受一個單一參數(最初函數的第一個參數)的函數,而且返回接受餘下的參數並且返回結果的新函數的技術,是高階函數的一種用法。好比求和函數add(1,2,3)
, 通過柯里化後變成add(1)(2)(3)
function currying(fn,...args){ if(fn.length <= args.length){ return fn(...args) } return function(...args1){ return currying(fn,...args,...args1) } } function add(a,b,c){ return a + b + c } add(1,2,3) // 6 var curryingAdd = currying(add); curryingAdd(1)(2)(3) // 6
有錯誤之處還請小夥伴們及時指出,以避免誤人子弟。想看往期內容,翻到頁面最上面有連接~