使用JS簡單實現apply、call和bind方法的實例代碼
1.方法介紹
apply、call和bind都是系統提供給我們的內置方法,每個函數都可以使用這三種方法,是因為apply、call和bind都實現在瞭Function的原型上(Function.prototype),而他們的作用都是給我們函數調用時顯式綁定上this。下面先介紹一下它們的基本用法:
apply方法:調用一個具有給定this值的函數,以及以一個數組(或類數組對象)的形式提供的參數。
使用語法:func.apply(thisArg, [argsArray])
thisArg:在func函數調用時綁定的this值;[argsArray]:一個數組或者類數組對象,其中的數組元素將作為單獨的參數傳給func函數;
使用效果:
function foo(x, y ,z) { console.log(this, x, y, z) } const obj = { name: 'curry', age: 30 } /** * 1.將obj對象綁定給foo函數的this * 2.數組中的1 2 3分別傳遞給foo函數對應的三個參數 */ foo.apply(obj, [1, 2, 3])
call方法:使用一個指定的 this值和單獨給出的一個或多個參數來調用一個函數。
使用語法:func.call(thisArg, arg1, arg2, ...)
thisArg:在func函數調用時綁定的this值;arg1, arg2, …:指定的參數列表,將作為參數傳遞給func函數;
使用效果:
function foo(x, y ,z) { console.log(this, x, y, z) } const obj = { name: 'curry', age: 30 } /** * 1.將obj對象綁定給foo函數的this * 2.call剩餘參數中的a b c分別傳遞給foo函數對應的三個參數 */ foo.call(obj, 'a', 'b', 'c')
bind方法:創建一個新的函數,在bind()被調用時,這個新函數的this被指定為bind()的第一個參數,而其餘參數將作為新函數的參數,供調用時使用。
使用語法:func.bind(thisArg[, arg1[, arg2[, ...]]])
thisArg:調用func函數時作為this參數傳遞給目標函數的值;arg1, arg2, …:當目標函數被調用時,被預置入func函數的參數列表中的參數;
使用效果:
function foo(...args) { console.log(this, ...args) } const obj = { name: 'curry', age: 30 } /** * 1.將obj對象綁定給foo函數的this * 2.bind剩餘參數中的1 2 3分別傳遞給foo函數中參數 * 3.也可在newFoo調用時傳入參數,這時bind傳遞的參數會與newFoo調用時傳遞的參數進行合並 */ const newFoo = foo.bind(obj, 1, 2, 3) newFoo() newFoo('a', 'b', 'c')
總結:
apply和call主要用於在函數調用時給函數的this綁定對應的值,兩者作用類似,主要區別就是除瞭第一個參數,apply方法接受的是一個參數數組,而call方法接受的是參數列表。
bind也是給函數指定this所綁定的值,不同於apply和call的是,它會返回一個新的函數,新函數中的this指向就是我們所指定的值,且分別傳入的參數會進行合並。
2.apply、call和bind方法的實現
為瞭所有定義的函數能夠使用我們自定義的apply、call和bind方法,所以需要將自己實現的方法掛在Function的原型上,這樣所有的函數就可以通過原型鏈找到自定義的這三個方法瞭。
2.1.apply的實現
Function.prototype.myApply = function(thisArg, argArray) { // 1.獲取當前需要被執行的函數 // 因為myApply是需要被當前函數進行調用的,根據this的隱式綁定,此處的this就是指向當前需要被執行的函數 const fn = this // 2.對傳入的thisArg進行邊界判斷 if (thisArg === null || thisArg === undefined) { // 當傳入的是null或者undefined是,被執行函數的this直接指向全局window thisArg = window } else { // 將傳入的thisArg對象化,方便後面在thisArg添加屬性 thisArg = Object(thisArg) } // 也可簡單寫成三元運算符: // thisArg = (thisArg === null || thisArg === undefined) ? window : Object(thisArg) // 3.將獲取的fn添加到thisArg對象上 // 這裡使用Symbol的原因是避免外部傳入的thisArg中的屬性與添加fn有沖突 const fnSymbol = Symbol() Object.defineProperty(thisArg, fnSymbol, { enumerable: false, configurable: true, writable: false, value: fn }) // 也可簡單寫成 // thisArg[fnSymbol] = fn // 4.對argArray進行判斷 // 看是否有傳入值,沒有值傳入就默認 [] argArray = argArray || [] // 5.調用獲取的fn函數,並將對應傳入的數組展開傳遞過去 const result = thisArg[fnSymbol](...argArray) // 調用完後刪除添加的屬性 delete thisArg[fnSymbol] // 6.將結果返回 return result }
測試:雖然打印出來的對象中還存在Symbol屬性,實際上已經通過delete刪除瞭,這裡是對象引用的問題。
function foo(x, y, z) { console.log(this, x, y, z) } foo.myApply({name: 'curry'}, [1, 2, 3])
2.2.call的實現
call方法的實現和apply方法的實現差不多,主要在於後面參數的處理。
Function.prototype.myCall = function(thisArg, ...args) { // 1.獲取當前需要被執行的函數 const fn = this // 2.對傳入的thisArg進行邊界判斷 thisArg = (thisArg === null || thisArg === undefined) ? window : Object(thisArg) // 3.將獲取的fn添加到thisArg對象上 const fnSymbol = Symbol() thisArg[fnSymbol] = fn // 4.調用獲取的fn函數,並將對應傳入的args傳遞過去 const result = thisArg[fnSymbol](...args) // 調用完後刪除添加的屬性 delete thisArg[fnSymbol] // 5.將結果返回 return result }
測試:
function foo(x, y, z) { console.log(this, x, y, z) } foo.myCall({name: 'curry'}, 1, 2, 3)
2.3.bind的實現
bind方法的實現稍微復雜一點,需要考慮到參數合並的問題。
Function.prototype.myBind = function(thisArg, ...argsArray) { // 1.獲取當前的目標函數,也就是當前使用myBind方法的函數 const fn = this // 2.對傳入的thisArg進行邊界判斷 thisArg = (thisArg === null || thisArg === undefined) ? window : Object(thisArg) // 3.將獲取的fn添加到thisArg對象上 const fnSymbol = Symbol() thisArg[fnSymbol] = fn // 4.定義一個新的函數 function newFn(...args) { // 4.1.合並myBind和newFn傳入的參數 const allArgs = [...argsArray, ...args] // 4.2.調用真正需要被調用的函數,並將合並後的參數傳遞過去 const result = thisArg[fnSymbol](...allArgs) // 4.3.調用完後刪除添加的屬性 delete thisArg[fnSymbol] // 4.4.將結果返回 return result } // 6.將新函數返回 return newFn }
測試:
function foo(x, y, z) { console.log(this, x, y, z) } const newFoo = foo.myBind({ name: 'curry' }, 1, 2) newFoo(3)
總結
到此這篇關於使用JS簡單實現apply、call和bind方法的文章就介紹到這瞭,更多相關JS實現apply、call和bind方法內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- 一文瞭解JavaScript中call/apply/bind的使用
- JS中call(),apply(),bind()函數的區別與用法詳解
- JS 函數的 call、apply 及 bind 超詳細方法
- JavaScript中的this例題實戰總結詳析
- JavaScript函數之call、apply以及bind方法案例詳解