apply?call?bind方法原理及使用場景示例詳解
正文
call、apply 和 bind 是掛在 Function 對象上的三個方法,調(diào)用這三個方法的必須是一個函數(shù)。
基本用法如下:
func.call(thisArg, param1, param2, ...) func.apply(thisArg, [param1,param2,...]) func.bind(thisArg, param1, param2, ...)
其中 func 是要調(diào)用的函數(shù),thisArg 一般為 this 所指向的對象,后面的 param1、2 為函數(shù) func 的多個參數(shù),如果 func 不需要參數(shù),則后面的 param1、2 可以不寫。
這三個方法共有的、比較明顯的作用就是,都可以改變函數(shù) func 的 this 指向。call 和 apply 的區(qū)別在于,傳參的寫法不同:apply 的第 2 個參數(shù)為數(shù)組; call 則是從第 2 個至第 N 個都是給 func 的傳參;而 bind 和這兩個(call、apply)又不同,bind 雖然改變了 func 的 this 指向,但不是馬上執(zhí)行,而這兩個(call、apply)是在改變了函數(shù)的 this 指向之后立馬執(zhí)行。
這三個方法的理念都是借用
方法的思路,例如A 對象有個 getName 的方法,B 對象也需要臨時使用同樣的方法,那么這時候我們是單獨為 B 對象擴展一個方法,還是借用一下 A 對象的方法呢?當然是可以借用 A 對象的 getName 方法,既達到了目的,又節(jié)省重復(fù)定義,節(jié)約內(nèi)存空間。
方法/特征 | call | apply | bind |
---|---|---|---|
方法參數(shù) | 多個 | 單個數(shù)組 | 多個 |
方法功能 | 函數(shù)調(diào)用改變this | 函數(shù)調(diào)用改變this | 函數(shù)調(diào)用改變this |
返回結(jié)果的 | 直接執(zhí)行的 | 直接執(zhí)行 | 返回待執(zhí)行函數(shù) |
底層實現(xiàn) | 通過eval | 通過eval | 間接調(diào)用apply |
應(yīng)用場景
我們來看看應(yīng)用場景有哪些?
判斷數(shù)據(jù)類型
用 Object.prototype.toString 來判斷類型是最合適的,借用它我們幾乎可以判斷所有類型的數(shù)據(jù)。
function getType(obj){ let type = typeof obj; if (type !== "object") { return type; } return Object.prototype.toString.call(obj).replace(/^$/, '$1'); }
判斷數(shù)據(jù)類型就是借用了 Object 的原型鏈上的 toString 方法,最后返回用來判斷傳入的 obj 的字符串,來確定最后的數(shù)據(jù)類型。
類數(shù)組借用方法
類數(shù)組因為不是真正的數(shù)組,所有沒有數(shù)組類型上自帶的種種方法,所以我們就可以利用一些方法去借用數(shù)組的方法,例如:
var arrayLike = { 0: 'java', 1: 'script', length: 2 } Array.prototype.push.call(arrayLike, 'jack', 'lily'); console.log(typeof arrayLike); // 'object' console.log(arrayLike); // {0: "java", 1: "script", 2: "jack", 3: "lily", length: 4}
arrayLike 是一個對象,模擬數(shù)組的一個類數(shù)組。從數(shù)據(jù)類型上看,它是一個對象。從上面的代碼中可以看出,用 typeof 來判斷輸出的是 'object',它自身是不會有數(shù)組的 push 方法的,這里我們就用 call 的方法來借用 Array 原型鏈上的 push 方法,可以實現(xiàn)一個類數(shù)組的 push 方法,給 arrayLike 添加新的元素。
獲取數(shù)組的最大/最小值
我們可以用 apply 來實現(xiàn)數(shù)組中判斷最大 / 最小值,apply 直接傳遞數(shù)組作為調(diào)用方法的參數(shù),也可以減少一步展開數(shù)組,可以直接使用 Math.max、Math.min 來獲取數(shù)組的最大值 / 最小值,例如:
let arr = [13, 6, 10, 11, 16]; const max = Math.max.apply(Math, arr); const min = Math.min.apply(Math, arr); console.log(max); // 16 console.log(min); // 6
apply和call的實現(xiàn)
apply 和 call 基本原理是差不多的,只是參數(shù)存在區(qū)別。
Function.prototype.call = function (context, ...args) { var context = context || window; context.fn = this; var result = eval('context.fn(...args)'); delete context.fn return result; } Function.prototype.apply = function (context, args) { let context = context || window; context.fn = this; let result = eval('context.fn(...args)'); delete context.fn return result; }
實現(xiàn) call 和 apply 的關(guān)鍵就在 eval 這行代碼。其中顯示了用 context 這個臨時變量來指定上下文,然后還是通過執(zhí)行 eval 來執(zhí)行 context.fn 這個函數(shù),最后返回 result。
要注意這兩個方法和 bind 的區(qū)別就在于,這兩個方法是直接返回執(zhí)行結(jié)果,而 bind 方法是返回一個函數(shù),因此這里直接用 eval 執(zhí)行得到結(jié)果。
bind的實現(xiàn)
bind 的實現(xiàn)思路基本和 apply 一樣,但是在最后實現(xiàn)返回結(jié)果這里,bind 和 apply 有著比較大的差異,bind 不需要直接執(zhí)行,因此不再需要用 eval ,而是需要通過返回一個函數(shù)的方式將結(jié)果返回,之后再通過執(zhí)行這個結(jié)果,得到想要的執(zhí)行效果。
Function.prototype.bind = function (context, ...args) { if (typeof this !== "function") { throw new Error("this must be a function"); } var self = this; var fbound = function () { self.apply(this instanceof self ? this : context, args.concat(Array.prototype.slice.call(arguments))); } if(this.prototype) { fbound.prototype = Object.create(this.prototype); } return fbound; }
實現(xiàn) bind 的核心在于返回的時候需要返回一個函數(shù),故這里的 fbound 需要返回,但是在返回的過程中原型鏈對象上的屬性不能丟失。因此這里需要用Object.create 方法,將 this.prototype 上面的屬性掛到 fbound 的原型上面,最后再返回 fbound。這樣調(diào)用 bind 方法接收到函數(shù)的對象,再通過執(zhí)行接收的函數(shù),即可得到想要的結(jié)果。
以上就是apply call bind方法原理及使用場景示例詳解的詳細內(nèi)容,更多關(guān)于apply call bind原理的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
微信小程序 網(wǎng)絡(luò)請求(GET請求)詳解
這篇文章主要介紹了微信小程序 網(wǎng)絡(luò)請求(GET請求)詳解的相關(guān)資料,需要的朋友可以參考下2016-11-11You-Dont-Know-JS詞法作用域及兩種常見的模型學(xué)習文檔
這篇文章主要為大家介紹了JS?詞法作用域及兩種常見的模型詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-08-08