實現call方法
call() 方法
使用一個指定的 this 值和單獨給出的一個或多個參數來調用一個函數。語法:
function.call(thisArg, arg1, arg2, ...)
call()
的原理比較簡單,首先要明確函數的this會指向它的直接調用者,我們變更調用者即完成this指向的變更:
//變更函數調用者示例
function printName() {
console.log(this.name)
}
// 測試
const person = {
name: 'Neeky'
}
person.printName = printName // 變更printName的調用者,將其this指向person
person.printName() // 'Neeky'
基於以上的代碼原理, 我們能很快的實現一個call()
Function.prototype.myCall = function(thisArg, ...args) {
thisArg.fn = this // this指向調用call的對象,即我們要改變this指向的函數
return thisArg.fn(...args) // 執行函數並return其執行結果
}
其原理就是,我們爲傳入的thisArg對象新增了一個fn函數,並且通過 thisArg.fn = this 這個操作複製了調用myCall的函數,因爲函數的this指向直接調用者。然後執行該fn函數返回其執行結果。
但是我們還需要處理一些細節:
Function.prototype.myCall = function(thisArg, ...args) {
const fn = Symbol('fn') // 聲明一個獨有的Symbol屬性, 防止fn覆蓋已有屬性
thisArg = thisArg || window // 若沒有傳入this, 默認綁定window對象
thisArg[fn] = this // this指向調用call的對象,即我們要改變this指向的函數
const result = thisArg[fn](...args) // 執行當前函數
delete thisArg[fn] // 刪除我們聲明的fn屬性
return result // 返回函數執行結果
}
//測試
printName.myCall(person) // 輸出'Neeky'
實現apply方法
apply() 方法調用一個具有給定this值的函數,以及作爲一個數組(或類似數組對象)提供的參數。
語法:func.apply(thisArg, [argsArray])
apply()
和call()
類似,區別在於call()接收參數列表,而apply()接收一個參數數組,所以我們在call()的實現上簡單改一下入參形式即可
Function.prototype.myApply = function(thisArg, args) {
const fn = Symbol('fn') // 聲明一個獨有的Symbol屬性, 防止fn覆蓋已有屬性
thisArg = thisArg || window // 若沒有傳入this, 默認綁定window對象
thisArg[fn] = this // this指向調用call的對象,即我們要改變this指向的函數
const result = thisArg[fn](...args) // 執行當前函數(此處說明一下:雖然apply()接收的是一個數組,但在調用原函數時,依然要展開參數數組。可以對照原生apply(),原函數接收到展開的參數數組)
delete thisArg[fn] // 刪除我們聲明的fn屬性
return result // 返回函數執行結果
}
//測試
printName.myApply(person, []) // 輸出'Neeky'
實現bind方法
bind()
方法創建一個新的函數,在 bind() 被調用時,這個新函數的 this 被指定爲 bind() 的第一個參數,而其餘參數將作爲新函數的參數,供調用時使用。
語法: function.bind(thisArg, arg1, arg2, ...)
首先明確bind方法做了什麼事情
- bind()除了this還接收其他參數,bind()返回的函數也接收參數,這兩部分的參數都要傳給返回的函數
- new會改變this指向:如果bind綁定後的函數被new了,那麼this指向會發生改變,指向當前函數的實例
- 沒有保留原函數在原型鏈上的屬性和方法
Function.prototype.myBind = function (thisArg, ...args) {
var self = this
// new優先級
var fbound = function () {
self.apply(this instanceof self ? this : thisArg, args.concat(Array.prototype.slice.call(arguments)))
}
// 繼承原型上的屬性和方法
fbound.prototype = Object.create(self.prototype);
return fbound;
}
//測試
const person = { name: 'Neeky' }
function printName() {
console.log(this.name)
console.log(arguments)
}
printName.myBind(person, 'a', 'b', 'c')() //輸出 Neeky ['a', 'b', 'c']
參考文檔:2萬字 | 前端基礎拾遺90問