前端基礎之手撕代碼(一)

前端學到如今,發現平時有些代碼平時看了千八百遍,都快要看吐了的代碼關鍵時刻上竟然寫不出來了,這真的是有點難受啊。因此就在這裏作一些總結,後面忘了能夠及時複習。 本系列大體分爲四篇:javascript

  1. 第一篇 call、apply、bind、new、柯里化、函數組合、防抖、節流
  2. 第二篇 數組去重、數組扁平化、千分位、深拷貝、promise完整實現(正在努力中)
  3. 第三篇 經常使用排序算法(正在努力中)
  4. 第四篇 大廠面試中常常出現的算法題(正在努力中)

1. Function.prototype.call

call方法使用一個指定的 this 值和單獨給出的一個或多個參數來調用一個函數。
call方法接受的是一個參數列表。
返回值是函數執行後的結果。前端

Function.prototype.myCall = function(context, ...args) {
    // 設置第一個參數的默認值爲window
    const ctx = context || window;
    // 設置一個惟一變量,防止覆蓋原屬性
    const func = Symbol('func');
    ctx[func] = this;
    const result = ctx[func](...args);
    // 執行後獲得返回值以前先刪除本次聲明的屬性
    delete ctx[func];
    return result;
}
複製代碼

2. Function.prototype.apply

apply方法使用一個指定的this值和單獨給出的一個參數來調用一個函數。
apply方法接受的參數是一個數組。
返回值是函數執行後的結果。java

// 與call的差別就體如今args這裏,使用call和apply以後,原函數接收到的參數都應該是解構後的參數
Function.prototype.myApply = function(context, args) {
    // 設置第一個參數的默認值爲window
    const ctx = context || window;
    // 設置一個惟一變量,防止覆蓋原屬性
    const func = Symbol('func');
    ctx[func] = this;
    const result = ctx[func](...args);
    // 執行後獲得返回值以前先刪除本次聲明的屬性
    delete ctx[func];
    return result;
}
複製代碼

3. Function.prototype.bind

bind方法接受參數,原函數也接受參數,兩部分參數都須要注意保留。
須要注意的是bind方法轉換後的函數能夠當作構造函數使用,因此須要注意this的指向。
注意保留原函數在原型鏈上的屬性和方法。 返回值是一個新的函數。面試

// 本身實現
Function.prototype.myBind = function(context, ...args) {
    const that = this;
    const funcBind = function() {
        // 當bind轉換後的函數被實例化了,apply要指向函數的實例
        const target = this instanceof funcBind ? this : context;
        const applyArgs = args.concat([...arguments]);
        that.apply(target, applyArgs);
    }
    // apply不保留函數原型上的方法,這裏作一層淺拷貝
    funcBind.prototype = Object.create(that.prototype);
    return funcBind;
}
// [MDN源碼實現](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/bind)
// Does not work with `new funcA.bind(thisArg, args)`
if (!Function.prototype.bind) (function(){
  var slice = Array.prototype.slice;
  Function.prototype.bind = function() {
    var thatFunc = this, thatArg = arguments[0];
    var args = slice.call(arguments, 1);
    if (typeof thatFunc !== 'function') {
      // closest thing possible to the ECMAScript 5
      // internal IsCallable function
      throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
    }
    return function(){
      var funcArgs = args.concat(slice.call(arguments))
      return thatFunc.apply(thatArg, funcArgs);
    };
  };
})();
複製代碼

4. new

建立一個用戶定義的對象類型的實例或具備構造函數的內置對象的實例。MDN釋義算法

  1. 建立一個空對象
  2. 使這個新對象繼承其構造函數的原型
  3. 執行構造函數並綁定新的this指向
  4. 若是該函數自己有返回對象則直接返回,不然返回新建立的對象
function myNew(func, ...args) {
    // Object.create()方法建立一個新對象,使用現有的對象來提供新建立的對象的__proto__
    // 至關於obj.__proto__ = func.prototype
    let obj = Object.create(func.prototype);
    // args是構造方法的入參, 由於這裏用myNew模擬, 因此入參從myNew傳入
    let result = func.apply(obj, args);
    return Object.prototype.toString.call(result) === '[object Object]' ? result : obj
}
複製代碼

5. 柯里化 curry

柯里化是指將一個接受多個參數的函數轉換爲固定部分參數,而後返回一個接受剩餘參數的函數。
function.length 屬性指明函數的形參個數。數組

function curry(fn) {
    return function recursive(...args) {
        if (fn.length > args.length) {
            return (...rest) => recursive(...args, ...rest);
        } else {
            return fn(...args);
        }
    }
}
複製代碼

6. 函數組合 compose

函數組合是指一個函數執行完以後把返回的結果再做爲參數賦給下一個函數來執行。
函數管道是從左往右函數執行,函數組合是從右往左執行。promise

function compose(...args) {
  return subArgs => {
    return args.reverse().reduce((total, func, index) => {
      return func(total);
    }, subArgs);
  }
}
複製代碼

7. 防抖函數 debounce

防抖是指短期內大量觸發同一事件,只會在最後一次事件完成後延遲執行一次函數。像是在用戶輸入用戶名的過程當中,須要對用戶名進行重複性的校驗,此時應該等待用戶中止輸入再校驗,否則會影響用戶體驗。
防抖實現的原理是觸發事件後設置一個定時器,在定時器延遲過程當中,若再次觸發事件,則重置定時器,直到沒有事件觸發後,定時器再觸發並執行對應函數。app

function debounce(func, delay = 200) {
    if (typeof func !== 'function') {
        throw Error('func必須是一個函數')
    };
    let timer = null;
    return function() {
        let context = this;
        let args = arguments;
        if (timer) clearTimeout(timer);
        timer = setTimeout(() => {
            func.apply(context, args)
        }, delay);
    };
};
複製代碼

8. 節流函數 throttle

節流是指每隔一段時間就執行一次函數。像是未擰緊的水龍頭同樣每隔一段時間滴一次水,即便在這段時間內水管裏的水再多,水龍頭也不會多滴一滴水。
節流的實現原理是觸發事件後設置一個定時器,在定時器延遲過程當中,即便再次觸發事件,也不會改變定時器的延遲時間,直到該定時器執行完成函數後,下次觸發事件纔會重置定時器。函數

function throttle(func, delay = 200) {
    if (typeof func !== 'function') {
        throw Error('func必須是一個函數')
    };
    let timer = null;
    return function() {
        let context = this;
        let args = arguments;
        if (!timer) {
            timer = setTimeout(() => {
                timer = null;
                func.apply(context, args);
            }, delay);
        };
    };
};
複製代碼
相關文章
相關標籤/搜索