博客 / 詳情

返回

“寒冬”三年經驗前端面試總結(含頭條、百度、餓了麼、滴滴等)之手寫題(二)

前言

不論是寒冬還是暖冬,找工作之前都需要做好充足的準備,面試的時候才能做到遊刃有餘。此文是把我最近找工作準備的以及筆試面試中涉及到的手寫題做一個總結。給自己,也給需要的同學。

手寫題是比較好準備的一個環節,大部分公司考察的題也就那麼多,大都不會超出範圍。

本文是手寫題系列的第二篇文章。


往期:

  1. "寒冬"三年經驗前端面試總結(含頭條、百度、餓了麼、滴滴等)
  2. "寒冬"三年經驗前端面試總結(含頭條、百度、餓了麼、滴滴等)之手寫題(一)
  3. "寒冬"三年經驗前端面試總結(含頭條、百度、餓了麼、滴滴等)之手寫題(promise篇)

實現eventEmitter

觀察者模式是我們工作中經常能接觸到的一種設計模式。用過 jquery 的應該對這種設計模式都不陌生。eventEmitternode 中的核心,主要方法包括on、emit、off、once

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 實現的功能,instanceof 運算符用於檢測構造函數的 prototype 屬性是否出現在某個實例對象的原型鏈上。其實考察的也是繼承。

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的過程

當我們new一個對象的時候,具體執行的是什麼?MDN上給的説明如下:

  1. 創建一個空的簡單 JavaScript 對象(即{});
  2. 鏈接該對象(即設置該對象的構造函數)到另一個對象 ;
  3. 將步驟1新創建的對象作為 this 的上下文 ;
  4. 如果該函數沒有返回對象,則返回 this

var child = new Parent()為例:

function newParent(){
    var obj = {}; // 首先創建一個對象
    obj.__proto__ = Parent.prototype; // 然後將該對象的__proto__屬性指向構造函數的protoType
    var result = Parent.call(obj) // 執行構造函數的方法,將obj作為this傳入
    return typeof(result) == 'object' ?  result : obj
}

lazyMan

原題如下:

實現一個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

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}})

函數currying

函數柯里化是把接受多個參數的函數變換成接受一個單一參數(最初函數的第一個參數)的函數,並且返回接受餘下的參數而且返回結果的新函數的技術,是高階函數的一種用法。比如求和函數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

寫在最後

有錯誤之處還請小夥伴們及時指出,以免誤人子弟。想看往期內容,翻到頁面最上面有鏈接~

user avatar 1023 頭像 buxia97 頭像 frontoldman 頭像 lesini 頭像 musicfe 頭像 joytime 頭像 mrqueue 頭像 lidalei 頭像 nihaojob 頭像 yiiouo 頭像 cipchk 頭像 fyuanlove 頭像
32 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.