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

funnyok發表於2021-09-09
前景

疫情無情人有情,在去年經歷網際網路一系列的風波之後,我相信大家有很多的小夥伴想在今年金三銀四的面試季中為自己的未來找一個好一點的公司。那麼今天我們來講解一下身為 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

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/3137/viewspace-2824845/,如需轉載,請註明出處,否則將追究法律責任。

相關文章