前端 P5 最基本應該掌握的代碼實現

前景

疫情無情人有情,在去年經歷互聯網一系列的風波以後,我相信你們有不少的小夥伴想在今年金三銀四的面試季中爲本身的將來找一個好一點的公司。那麼今天咱們來說解一下身爲 P5 工程師須要知道的一些原理及其如何親自手寫出它的實現過程,有可能咱們平常開發業務的時候用不上這些本身寫的方法,可是咱們咱們對原理一無所知,那麼在面試的時候一旦被深挖那麼可能離本身心念的公司就會又遠一步。前端

模式 call

  • 第一個參數爲 null或者 undefined 時,this 指向全局對象 window,值爲原始值的指向改原始值的自動包裝對象,如 String、number、boolean
  • 爲了不函數名與上下問(context)的屬性發生衝突,使用 Symbol 類型做爲惟一值
  • 將函數做爲傳入的上下文(context)屬性執行
  • 函數執行完後刪除改屬性
  • 返回執行結果
Function.prototype.myCall = function(context,...args){
    context = (context ?? window) || new Object(context);
    const key = Symbol();
    context[key] = this;
    const result = context[key](...args);
    delete context[key];
    return result;
}

模式 apply

  • 前部分與call同樣
  • 第二個參數能夠不傳,但類型必須爲數組或者類數組
Function.prototype.myApply = function(context) {
    context =  (context ?? window) || new Object(context)
    const key = Symbol()
    const args = arguments[1]
    context[key] = this
    let result
    if(args) {
        result = context[key](...args)
    } else {
        result = context[key]
    }
    delete context[key]
    return result
}

模式 bind

  • 使用 call / apply 指定 this
  • 返回一個綁定函數
  • 當返回的綁定函數做爲構造函數被new調用,綁定的上下文指向實例對象
  • 設置綁定函數的prototype 爲原函數的prototype
Function.prototype.myBind = function(context, ...args) {
    const fn = this
    const bindFn = function (...newFnArgs) {
        fn.call(
            this instanceof bindFn ? this : context,
            ...args, ...newFnArgs
        )
    }
    bindFn.prototype = Object.create(fn.prototype)
    return bindFn
}

深拷貝

  • 判斷類型是否爲原始類型,若是是,無需拷貝直接返回
  • 爲避免出現循環引用,拷貝對象時先判斷存儲空間中是否存在當前對象,若是有就直接返回
  • 開闢一個存儲空間,來存儲當前對象和拷貝對象的對應關係
  • 對引用類型遞歸拷貝直到屬性爲原始類型
const deepClone = (target, cache = new WeakMap()) => {
    if(target === null || typeof target !== 'object') {
        return target
    }
    if(cache.get(target)) {
        return target
    }
    const copy = Array.isArray(target) ? [] : {}
    cache.set(target, copy)
    Object.keys(target).forEach(key => copy[key] = deepClone(obj[key], cache))
    return copy
}

函數防抖

  • this繼承自父級上下文,指向觸發事件的目標元素
  • 事件被觸發時,傳入event對象
  • 傳入leading參數,判斷是否能夠當即執行回調函數,沒必要要等到事件中止觸發後纔開始執行
  • 回調函數能夠有返回值,須要返回執行結果
const debounce = (fn, wait = 300, leading = true) => {
    let timerId, result
    return function(...args) {
        timerId && clearTimeout(timerId)
        if (leading) {
            if (!timerId) result = fn.apply(this, args)
            timerId = setTimeout(() => timerId = null, wait)
        } else {
            timerId = setTimeout(() => result = fn.apply(this, args), wait)
        }
        return result
    }
}

函數節流(定時器)

函數中止觸發後 n妙後開始執行,中止觸發後繼續執行一次事件
const throttle = (fn, wait = 300) => {
    let timerId
    return function(...args) {
        if(!timerId) {
            timerId = setTimeout(() => {
                timerId = null
                return result = fn.apply(this, ...args)
            }, wait)
        }
    }
}

函數節流(時間戳)

函數觸發後馬上執行,中止觸發後不在執行事件面試

const throttle = (fn, wait = 300) => {
    let prev = 0
    let result
    return function(...args) {
        let now = +new Date()
        if(now - prev > wait) {
            prev = now
            return result = fn.apply(this, ...args)
        }
    }
}

ES6版繼承

ES5的繼承,實質是先創造子類的實例對象,而後將再將父類的方法添加到this上。ES6的繼承,先創造父類的實例對象(因此必須先調用super方法,而後再用子類的構造函數修改this。編程

class Super {    constructor(foo) {      this.foo = foo    }    printFoo() {      console.log(this.foo)    }  } 	  class Sub extends Super {    constructor(foo, bar) {      super(foo)      this.bar = bar    }  }js

上面做爲一部分都是做爲一個 P5應該具備基本技能,同時我這邊會陸續的更新一些列的面試題,對於 call、apply、bind 有什麼不清楚很差理解的地方能夠查看我以前的文章,讓你一篇文章完全搞清楚,這裏說了ES6繼承那麼後續我會結合面向對象編程和設計模式依次爲你們講解,關注 tu 老師說前端帶你玩轉全棧 圖片設計模式

相關文章
相關標籤/搜索