面試題裏的那些各類手寫

最近準備初級前端面試,發現有不少手寫實現什麼的,例如什麼手寫實現bind,promise。手寫ajax,手寫一些算法。
翻閱了不少書籍和博客。javascript

這裏作一個總結改進,算是對我後面大概爲期一個月找工做的準備。html

手寫實現bind()

Function.prototype._bind = function (context) {
            var self = this;
            var args = Array.prototype.slice.call(arguments, 1);
            var fBind = function () {
                var bindArgs = Array.prototype.slice.call(arguments);
                return self.apply(this instanceof fBind ? this : context, args.concat(bindArgs));
            }
            fBind.prototype = self.prototype&&Object.create(self.prototype)
            return fBind;
        }

簡單的說明:前端

  • 這裏之因此傳參的時候須要兩個數組,是由於考慮到用new以構造函數的形式調用硬綁定函數的狀況:this這時是應該指向實例對象的。
  • 這樣子就須要繼承以前函數的方法, fBind.prototype = self.prototype&&Object.create(self.prototype)

,同時也能夠用 Object.setPrototypeOf(fBind.prototype,self.prototype)
考慮到存在undefined的狀況,前面加一個判斷self.prototype&&.....java

  • 關於apply的第一個參數,若是考慮到以前的狀況,是不能傳入context的,這須要作一個判斷。

像是下面的狀況node

function Foo(price){ 
       
          this.price =price
            this.fn = ()=>{
                console.log('hi fn')
            }
             console.log(this.name)
        }

        Foo.prototype.sayMyName = function(){
            console.log(this.name)
        }
      var Obj1 = {
        name:'obj1'
      }
        var b =Foo._bind(Obj1)
        b() //"obj1"
        var c = new b(1000)//"i am c"
        c.name = 'i am c'
        c.sayMyName()

這裏的this的指向就是c,它指向實例對象自己es6

後面以這道題爲引線面試官可能會追問:面試

  • 什麼是執行上下文
  • this的判斷
  • call,bind的區別

手寫一個函數實現斐波那契數列

首先拷一個阮神在他es6教程裏的一個寫法。ajax

function* fibonacci() {
  let [prev, curr] = [0, 1];
  for (;;) {
    yield curr;
    [prev, curr] = [curr, prev + curr];
  }
}

for (let n of fibonacci()) {
  if (n > 1000) break;
  console.log(n);
}

更精簡的算法

const feibo= max=>{
    let [a,b,i]= [0,1,1]
    while(i++<=max) {
        [a,b] = [b,a + b ]
       console.log(b)
    }
  return a  
}

相對是很是簡單的,感受也不會多問啥,就很少說了。數組

手寫一個簡單的ajax

let xhr = new XMLHttpRequest()

        xhr.open('get', url, true)

        xhr.onreadystatechange = function(){
            if(xhr.readyState === 4){
            console.log('請求完成')
                if(this.status >= 200 &&this.status<300){ 
                    conso.log('成功')
                }else{
                    consol.log('失敗')
                }
            }
        }
         xhr.onerror = function(e) {
         console.log('鏈接失敗')
        }
        xhr.send()

大概是這麼個意思就差很少了,順勢可能會問一些狀態碼和狀態值的問題,或者直接問到關於http上面的問題。

原型繼承

function inherit(supertype,subtype){
            Object.setPrototypeOf(subtype.prototype,supertype.prototype)
            subtype.prototype.constructor = subtype
        }

        function Car(name,power){
            this.name=name
            this.power = power
        }

        Car.prototype.showPower = function(){
            console.log(this.power)
        }

        function Changan(price,name,power){
            this.price = price
            Car.call(this,name,power)
        }

        inherit(Car,Changan)

        Changan.prototype.showName = function(){
            console.log(this.name)
        }

        var star = new Changan(100000,'star',500)

        star.showPower()

防抖與節流

function debounce(fn,duration){
            var  timer
            window.clearTimeout(timer)
            timer = setTimeout(()=>{
                fn.call(this)
            },duration)
        }
  function throttle(fn,duration){
            let canIRun
            if(!canIRun)return
            fn.call(this)
            canIRun = false
            setTimeout(()=>{
                canIRun = true
            },duration)
        }

數組去重

我通常就用這兩種,大部分狀況都能應付了。

[...new Set(array)]
//hash
 function unique(array) {
      const object = {}
      array.map(number=>{
          object[number] = true
      })
      return Object.keys(object).map(//.......)
  }//大概是這樣的意思,寫法根據數組的不一樣可能會有所改變

深拷貝

應該是面試裏面手寫xxx出現頻率最高的題了,不管是筆試仍是面試。
老是讓你手寫實現深拷貝函數。

事實上,js並不能作到真正徹底的標準的深拷貝

因此無論你寫什麼樣的深拷貝函數都會有不適用的地方,這取決於使用的場景和拷貝的對象,若是面試官在這上面鑽研比較深的話,是很難作到完美的。

既然這樣就寫個將就一點的深拷貝吧,面向面試的那種。

function deepClone(item) {
      return result;
  }
  • 首先在類型判斷上作一個選擇,通常狀況來講,用new建立的實例對象用typeof判斷會出問題的,相比之下instanceof也不靠譜。這裏面相對比較靠譜的Object.prototype.toString.call(item)。(這個其實也不兼容到所有狀況和性能要求,可是面向面試代碼可讀性會高一點)。

    type = Object.prototype.toString.call(item).slice(8,this.length-1),
    //[object String],[object Array],[object Object]
  • 函數的拷貝,這裏不能使用bind,會改變this指向或影響後續使用call調用該拷貝的函數,大部分狀況是沒必要要的,這裏就直接賦值吧。
  • 因而這裏能夠把基本數據類型和Function放一塊兒。

    fk= ['String','Number','Boolean','Function'].indexOf(type)>-1
  • dom對象的拷貝: result = item.cloneNode(true);
  • 忽略正則
  • Date[object Object], [object Array]放到後面的判斷

    let other = {           //須要遞歸或者其餘的操做
                          Array() {
                              result = []
                              item.forEach((child, index)=>{
                                  hash.set(item, result);
                                  result[index] = deepClone(child,hash)
                              })
                          },
                          Date(){
                              result = new Date(item)
                          },
                          Object(){
                              result = {}
                              Reflect.ownKeys(item).forEach(child=>{
                                  hash.set(item, result);
                                  result[child] = deepClone(item[child],hash)
                              })
                          }
                      }
                      other[type]()

這樣子是否是相對清晰一些了,應付通常的狀況應該差很少了,可是沒考慮循環引用

這裏給個思路是使用ES6WeakMap,不知道的兄弟能夠看看阮神的ES6博客,爲防止爆棧,我把循環引用直接扔給它,完美拷貝。
就至關於

var wm = new WeakMap()

var obj = {
   name:null
 }
obj.name = obj
wm.set(obj,wm.get(obj))
console.log(wm)

如今就須要在開頭檢查一下循環引用,而後直接返回WeakMap對象鍵名爲item參數對象的值
因此最終代碼就是

function deepClone(item,hash = new WeakMap()) {
       if (!item) return item
       if (hash.has(item))return hash.get(item);  //檢查循環引用
           var result,
             type = Object.prototype.toString.call(item).slice(8,this.length-1),
             fk= ['String','Number','Boolean','Function'].indexOf(type)>-1

               if(fk){
                   result = item;//直接賦值
               }else if(item.nodeType && typeof item.cloneNode === "function"){
                   result = item.cloneNode(true);          //是不是dom對象
               }else{

                   let other = {           //須要遞歸或者其餘的操做
                       Array() {
                           result = []
                           item.forEach((child, index)=>{
                               hash.set(item, result);
                               result[index] = deepClone(child,hash)
                           })
                       },
                       Date(){
                           result = new Date(item)
                       },
                       Object(){
                           result = {}
                           Reflect.ownKeys(item).forEach(child=>{
                               hash.set(item, result);
                               result[child] = deepClone(item[child],hash)
                           })
                       }
                   }
                   other[type]()
               }
       return result;
   }

意思就大概是這個意思,固然深拷貝的方法有不少,甚至不必定用到遞歸。面試官總會有找茬的地方的。
我以爲我寫的這個仍是知足我如今找工做的級別要求的。

而後是我用來測試的對象

var obj1 = {
   name:'obj1',
   one : {
       a:new Date(),
       b:new String('1-2'),
       c:new Array(['this','is',1,{a:23}]),
       d: function () {
           if(true){
               return 'd'
           }
       },
       e:new Number(15),
       f:new Boolean(true)
   },
   two(x){
       console.log(x+' '+this.name)
   },
   three : [
       {
           a:'this is a',
            b:document.body,  
           c:{
               a:[1,2,3,4,5,[13,[3],true],10],
               b:{
                   a:{
                       a:[1,2,3]
                   },
                   b:2
               }
           }
       },
   ],
   four:[1,2]
}
    obj1.name=obj1
    obj1.four[3] = obj1
   var copy = deepClone(obj1)

   console.log(copy)
   copy.two.call(window,'hi')

## new

簡單說下大概是這麼一個過程

  • 建立一個空對象
  • 執行傳入的構造函數,執行過程當中對 this 操做就是對 這個空對象 進行操做。
  • 返回這個空對象

模擬須要考慮的問題

  • 是一個空對象,這裏面的寫法obj原型鏈是沒有上一級的,即不存在與其餘任何對象之間的聯繫,雖然在這裏面沒多少區別:var obj = Object.create(null),
  • this指向這個空對象:let rt = Constructor.apply(obj, arguments);
  • 能夠訪問構造函數的原型鏈, Object.setPrototypeOf(obj, Constructor.prototype);
  • 若是構造函數有返回值,而且是一個對象,那麼實例對象拿到的就是這個對象(應該只是值,並非引用)。因此這裏要作個判斷return typeof rt === 'object' ? rt : obj;

    最終的代碼

function _new(){
    var obj =  Object.create(null),
    Constructor = [].shift.call(arguments);
    Object.setPrototypeOf(obj, Constructor.prototype);
    let  rt = Constructor.apply(obj, arguments);
    return rt instanceof Object ? rt : obj;
}

<br/>
<br/>

快速排序

快排
:代碼精簡了一點

function quickSort(arr){
       if(arr.length<=1)return arr
       var index = Math.floor(arr.length/2),
           number = arr.splice(index,1)[0],
           left = [],
           right = [];
       arr.forEach(item=>{
        item<=number?left.push(item):right.push(item)
       })
       return quickSort(left).concat([number],quickSort(right))
   }

這期間會不斷更新並修改,這裏面的手寫實現您若是有更好的寫法或者新的思路,也但願能夠說明交流。最後謝謝大佬些的觀看。

相關文章
相關標籤/搜索