apply?call?bind方法原理及使用場(chǎng)景示例詳解
正文
call、apply 和 bind 是掛在 Function 對(duì)象上的三個(gè)方法,調(diào)用這三個(gè)方法的必須是一個(gè)函數(shù)。
基本用法如下:
func.call(thisArg, param1, param2, ...) func.apply(thisArg, [param1,param2,...]) func.bind(thisArg, param1, param2, ...)
其中 func 是要調(diào)用的函數(shù),thisArg 一般為 this 所指向的對(duì)象,后面的 param1、2 為函數(shù) func 的多個(gè)參數(shù),如果 func 不需要參數(shù),則后面的 param1、2 可以不寫。
這三個(gè)方法共有的、比較明顯的作用就是,都可以改變函數(shù) func 的 this 指向。call 和 apply 的區(qū)別在于,傳參的寫法不同:apply 的第 2 個(gè)參數(shù)為數(shù)組; call 則是從第 2 個(gè)至第 N 個(gè)都是給 func 的傳參;而 bind 和這兩個(gè)(call、apply)又不同,bind 雖然改變了 func 的 this 指向,但不是馬上執(zhí)行,而這兩個(gè)(call、apply)是在改變了函數(shù)的 this 指向之后立馬執(zhí)行。
這三個(gè)方法的理念都是借用
方法的思路,例如A 對(duì)象有個(gè) getName 的方法,B 對(duì)象也需要臨時(shí)使用同樣的方法,那么這時(shí)候我們是單獨(dú)為 B 對(duì)象擴(kuò)展一個(gè)方法,還是借用一下 A 對(duì)象的方法呢?當(dāng)然是可以借用 A 對(duì)象的 getName 方法,既達(dá)到了目的,又節(jié)省重復(fù)定義,節(jié)約內(nèi)存空間。
方法/特征 | call | apply | bind |
---|---|---|---|
方法參數(shù) | 多個(gè) | 單個(gè)數(shù)組 | 多個(gè) |
方法功能 | 函數(shù)調(diào)用改變this | 函數(shù)調(diào)用改變this | 函數(shù)調(diào)用改變this |
返回結(jié)果的 | 直接執(zhí)行的 | 直接執(zhí)行 | 返回待執(zhí)行函數(shù) |
底層實(shí)現(xiàn) | 通過eval | 通過eval | 間接調(diào)用apply |
應(yīng)用場(chǎng)景
我們來看看應(yīng)用場(chǎ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ù)組因?yàn)椴皇钦嬲臄?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 是一個(gè)對(duì)象,模擬數(shù)組的一個(gè)類數(shù)組。從數(shù)據(jù)類型上看,它是一個(gè)對(duì)象。從上面的代碼中可以看出,用 typeof 來判斷輸出的是 'object',它自身是不會(huì)有數(shù)組的 push 方法的,這里我們就用 call 的方法來借用 Array 原型鏈上的 push 方法,可以實(shí)現(xiàn)一個(gè)類數(shù)組的 push 方法,給 arrayLike 添加新的元素。
獲取數(shù)組的最大/最小值
我們可以用 apply 來實(shí)現(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的實(shí)現(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; }
實(shí)現(xiàn) call 和 apply 的關(guān)鍵就在 eval 這行代碼。其中顯示了用 context 這個(gè)臨時(shí)變量來指定上下文,然后還是通過執(zhí)行 eval 來執(zhí)行 context.fn 這個(gè)函數(shù),最后返回 result。
要注意這兩個(gè)方法和 bind 的區(qū)別就在于,這兩個(gè)方法是直接返回執(zhí)行結(jié)果,而 bind 方法是返回一個(gè)函數(shù),因此這里直接用 eval 執(zhí)行得到結(jié)果。
bind的實(shí)現(xiàn)
bind 的實(shí)現(xiàn)思路基本和 apply 一樣,但是在最后實(shí)現(xiàn)返回結(jié)果這里,bind 和 apply 有著比較大的差異,bind 不需要直接執(zhí)行,因此不再需要用 eval ,而是需要通過返回一個(gè)函數(shù)的方式將結(jié)果返回,之后再通過執(zhí)行這個(gè)結(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; }
實(shí)現(xiàn) bind 的核心在于返回的時(shí)候需要返回一個(gè)函數(shù),故這里的 fbound 需要返回,但是在返回的過程中原型鏈對(duì)象上的屬性不能丟失。因此這里需要用Object.create 方法,將 this.prototype 上面的屬性掛到 fbound 的原型上面,最后再返回 fbound。這樣調(diào)用 bind 方法接收到函數(shù)的對(duì)象,再通過執(zhí)行接收的函數(shù),即可得到想要的結(jié)果。
以上就是apply call bind方法原理及使用場(chǎng)景示例詳解的詳細(xì)內(nèi)容,更多關(guān)于apply call bind原理的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
使用?render?函數(shù)封裝高擴(kuò)展的組件
這篇文章主要介紹了使用?render?函數(shù)封裝高擴(kuò)展的組件,vue?官網(wǎng)給出的?render?函數(shù)的例子只能體現(xiàn)?render?函數(shù)的優(yōu)雅的一方面,卻不能看出其擴(kuò)展性,今天就來封裝一個(gè)體現(xiàn)其擴(kuò)展性的組件,需要的朋友可以參考一下2021-12-12微信小程序 網(wǎng)絡(luò)請(qǐng)求(GET請(qǐng)求)詳解
這篇文章主要介紹了微信小程序 網(wǎng)絡(luò)請(qǐng)求(GET請(qǐng)求)詳解的相關(guān)資料,需要的朋友可以參考下2016-11-11You-Dont-Know-JS詞法作用域及兩種常見的模型學(xué)習(xí)文檔
這篇文章主要為大家介紹了JS?詞法作用域及兩種常見的模型詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-08-08