探究call、bind、apply知識點

call、bind、apply知識點

一、食用方式

  • apply
function.apply(obj, [param1,params2,...])
// obj:要綁定的this
// 第二個參數:類數組或數組,作爲function的參數傳入
// 立即執行
  • call
function.call(obj, param1, param2, ...)
// obj:要綁定的this
// 第二個參數:函數運行的參數,用逗號隔開
// 立即執行
  • bind
function.bind(obj, param1, param2, ...)
// obj:要綁定的this
// 第二個參數:函數運行的參數,用逗號隔開
// 返回一個函數

二、使用場景

基本理念:借用方法,修改this指向

  • 獲取數據類型
const params = 'ahwgs'
const toString = Object.prototype.toString
const type = toString.call(params)
console.log('數據類型',type) // [object String]
  • 類數組借用數組的方法
var arrayLike = {
  0: 'OB',
  1: 'Koro1',
  length: 2
}
Array.prototype.push.call(arrayLike, '添加元素1', '添加元素2');
console.log(arrayLike) // {"0":"OB","1":"Koro1","2":"添加元素1","3":"添加元素2","length":4}

借用數組的push方法,向arrayLikepush新數據

三、手寫實現

  • 手寫call實現
Function.prototype.myCall = function(context,...arr) {
    if (context === null || context === undefined) {
     // 指定爲 null 和 undefined 的 this 值會自動指向全局對象(瀏覽器中爲window)
     context = window
    } else {
     context = Object(context) // 值爲原始值(數字,字符串,布爾值)的 this 會指向該原始值的實例對象
    }
    const specialPrototype = Symbol('特殊屬性Symbol') // 用於臨時儲存函數
    context[specialPrototype] = this; // 函數的this指向隱式綁定到context上
    let result = context[specialPrototype](...arr); // 通過隱式綁定執行函數並傳遞參數
    delete context[specialPrototype]; // 刪除上下文對象的屬性
    return result; // 返回函數執行結果
 }
  • 手寫apply
Function.prototype.myApply = function (context) {
    if (context === null || context === undefined) {
      context = window // 指定爲 null 和 undefined 的 this 值會自動指向全局對象(瀏覽器中爲window)
    } else {
      context = Object(context) // 值爲原始值(數字,字符串,布爾值)的 this 會指向該原始值的實例對象
    }
    // JavaScript權威指南判斷是否爲類數組對象
    function isArrayLike(o) {
      if (o &&                                    // o不是null、undefined等
        typeof o === 'object' &&                // o是對象
        isFinite(o.length) &&                   // o.length是有限數值
        o.length >= 0 &&                        // o.length爲非負值
        o.length === Math.floor(o.length) &&    // o.length是整數
        o.length < 4294967296)                  // o.length < 2^32
        return true
      else
        return false
    }
    const specialPrototype = Symbol('特殊屬性Symbol') // 用於臨時儲存函數
    context[specialPrototype] = this; // 隱式綁定this指向到context上
    let args = arguments[1]; // 獲取參數數組
    let result
    // 處理傳進來的第二個參數
    if (args) {
      // 是否傳遞第二個參數
      if (!Array.isArray(args) && !isArrayLike(args)) {
        throw new TypeError('myApply 第二個參數不爲數組並且不爲類數組對象拋出錯誤');
      } else {
        args = Array.from(args) // 轉爲數組
        result = context[specialPrototype](...args); // 執行函數並展開數組,傳遞函數參數
      }
    } else {
      result = context[specialPrototype](); // 執行函數
    }
    delete context[specialPrototype]; // 刪除上下文對象的屬性
    return result; // 返回函數執行結果
};
  • 手寫bind
Function.prototype.myBind = function (objThis, ...params) {
    const thisFn = this; // 存儲源函數以及上方的params(函數參數)
    // 對返回的函數 secondParams 二次傳參
    let fToBind = function (...secondParams) {
        const isNew = this instanceof fToBind // this是否是fToBind的實例 也就是返回的fToBind是否通過new調用
        const context = isNew ? this : Object(objThis) // new調用就綁定到this上,否則就綁定到傳入的objThis上
        return thisFn.call(context, ...params, ...secondParams); // 用call調用源函數綁定this的指向並傳遞參數,返回執行結果
    };
    if (thisFn.prototype) {
        // 複製源函數的prototype給fToBind 一些情況下函數沒有prototype,比如箭頭函數
        fToBind.prototype = Object.create(thisFn.prototype);
    }
    return fToBind; // 返回拷貝的函數
};

關於

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章