call
call 方法使用一個函數執行的時候更改本身 this 指向,並傳入一個或者多個參數。
var obj = { name: '$call' } function _fun() { console.log(this.name, ...arguments) } _fun.call(obj, 'call1', 'call2', 'call3') // $call call1 call2 call3
內部實現原理:
Function.prototype.$$call = function (context) { // 第一個參數為 this 指向值,如無則指向 window context = context || window // 將本身的函數保存下來,在后面需要執行,這一步 this 的指向已經指向了 context context.fn = this // 將后面傳入的參數轉為數組,取除第一個 this 指向剩下的所有參數 let args = [...arguments].slice(1) // 執行函數本身,並將參數傳入 let result = context.fn(...args) // 銷毀函數,避免作用域污染 delete context.fn return result }
apply
apply 方法同 call 一樣使用一個函數執行的時候更改本身 this 指向,只是傳參的時候只有一個,並且必須是數組(如果call與apply傳參類型記不清,可以根據方法的第一個字母來區分,apply -> a(首字母) -> array)。
var obj = { name: '$call' } function _fun() { console.log(this.name, ...arguments) } _fun.apply(obj, ['call1', 'call2', 'call3']) // $call call1 call2 call3
內部實現原理:
Function.prototype.$$apply = function (context) { // 第一個參數為 this 指向值,如無則指向 window context = context || window // 將本身的函數保存下來,在后面需要執行,這一步 this 的指向已經指向了 context context.fn = this // 將后面傳入的參數轉為數組,取除第二個參數 let args = [...arguments][1] // 如果第二個參數不是對象則報錯 if (typeof args !== 'object') { throw Error('CreateListFromArrayLike called on non-object') return } // 執行函數本身,並將參數傳入 let result = context.fn(...args) // 銷毀函數,避免作用域污染 delete context.fn return result }
bind
bind 方法與 call 和 apply 不同的點是后續的參數沒有要求,但是 bind 會返回一個 this 指向已改變的函數,相同的是第一個參數就是 this 指向值
var obj = { name: '$call' } function _fun() { console.log(this.name, ...arguments) } _fun.call(obj, ['call1'], 'call3')('call2') // $call ["call1"] call3 call2
內部實現原理:
Function.prototype.$$bind = function (context) { // 取bind執行的時候除第一個后續的所有參數 let args = [...arguments].slice(1) // 函數本身緩存 let _this = this // 返回函數 return function () { // 合並返回函數執行傳入的參數 let bindArg = [...args, ...arguments] // 再次調用時的函數本身執行 return _this.call(context, ...bindArg) } }