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方法,向arrayLike
中push
新數據
三、手寫實現
- 手寫
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; // 返回拷貝的函數
};
關於
- 本文首發於探究call、bind、apply知識點
- 參考前端面試之道