call() apply() bind() 都是改變this指向的方法
call() apply() bind() 的第一個參數都是this的指向對象,后面的參數是給調用的方法傳參
背景~例如 有以下代碼:
var name = "window-name", age = 1000, skil = "anyThing" ; var originPerson = { name: 'liLi', age: '18', skil: 'dance' } function print(valOne,valTwo){ console.log("name:",this.name) console.log("age:",this.age) console.log("skil:",this.skil) console.log("valOne:",valOne) console.log("valTwo:",valTwo) }
調用print,並傳兩個參數1和2:
print(1,2) //name: window-name //age: 1000 //skil: anyThing //valOne: 1 //valTwo: 2
第一部分 call(),apply(),bind()的區別:
1:call(), apply() 立即執行,bind()要調用再執行
上代碼:
print.call(originPerson) // name: liLi // age: 18 // skil: dance // valOne: undefined // valTwo: undefined print.apply(originPerson) // name: liLi // age: 18 // skil: dance // valOne: undefined // valTwo: undefined print.bind(originPerson) // 此處未打印出東西 print.bind(originPerson)() // name: liLi // age: 18 // skil: dance // valOne: undefined // valTwo: undefined
由上代碼可見,print.bind()之后,並沒有打印出東西,要再調用之后才執行print()方法
注:這里沒有給print()傳參,所以valOne, valTwo 打印都是undefined,那么傳參要怎么傳呢?這就要講一下第二點區別了:
2. call(),apply(),bind()第一個參數后面參數的傳遞方式不一樣了:
call(),bind()的第二個,第三個,第四個...參數,是用逗號隔開,傳遞的
apply() 需要把多個參數放在一個數組中,作為第二個參數傳遞
上代碼:
print.call(originPerson,"call1","call2") // name: liLi // age: 18 // skil: dance // valOne: call1 // valTwo: call2 print.apply(originPerson,"apply1","apply2") // Uncaught TypeError print.apply(originPerson,["apply1","apply2"]) // name: liLi // age: 18 // skil: dance // valOne: apply1 // valTwo: apply2 print.bind(originPerson,"bind1","bind2")() // name: liLi // age: 18 // skil: dance // valOne: bind1 // valTwo: bind2
由上代碼可見,apply傳多個參數會報錯,需要把多個參數放在一個數組中傳
第二部分:手動實現以上三個方法
1:手動實現call方法
上代碼:
Function.prototype.myCall = function(quoteObj,...args){ let quote = quoteObj || window; // 新建一個唯一symbol變量,避免變量重復 let func = Symbol() // 將當前被調用的方法定義在quote.fun上 quote[func] = this; args = args ? args : [] const res = args.length > 0 ? quote[func](...args) : quote[func]() // 刪除該方法,不然會對傳入對象造成污染(添加該方法) delete quote[func] return res }
先來看實現效果:
print.myCall(originPerson,"call1","call2") // name: liLi // age: 18 // skil: dance // valOne: call1 // valTwo: call2
以上代碼的實現思路及解析:
首先確定方法的參數
第一個參數quoteObj:this的指向對象
Function.prototype.myCall = function(quoteObj,...args){
let quote = quoteObj || window;
let func = Symbol()
quote[func] = this;
console.log(quote)
可見print方法已經定義在了quote.fun上
而我們調用print方法時:
在當前print方法的作用域里找不到name這個值,就會向父級作用域里找,
此時print的父是quote,也就是先找我們傳入的第一個參數里有沒有name這個值,
如果有打印的就是quote里的值,沒有 就會打印出undefined
console.log(quote[func](...args)) // name: liLi // age: 18 // skil: dance // valOne: undefined // valTwo: undefined
這里做個容錯
args = args ? args : []
const res = args.length > 0 ? quote[func](...args) : quote[func]()
delete quote[func]
最后myCall方法是返回出res就好了
return res }
2:手動實現apply方法
由第一部分 call(),apply(),bind()的區別 我們知道:apply前部分與call一樣,第二個參數可以不傳,但類型必須為數組或者類數組
所以代碼實現如下:
Function.prototype.myApply = function(quoteObj,args = []){ let quote = quoteObj || window; // 新建一個唯一symbol變量,避免變量重復 let func = Symbol() // 將當前被調用的方法定義在quote.fun上 quote[func] = this; const res = args.length > 0 ? quote[func](...args) : quote[func]() // 刪除該方法,不然會對傳入對象造成污染(添加該方法) delete quote[func] return res }
實現效果:
print.myApply(originPerson,["apply1","apply2"]) // name: liLi // age: 18 // skil: dance // valOne: apply1 // valTwo: apply2
代碼實現思路跟call差不多 ,在此就不多做贅述了
3:手動實現bind方法
由第一部分 call(),apply(),bind()的區別 我們知道:
1)bind的傳參與call一樣,
2)bind創建的新函數可能傳入多個參數
所以代碼實現如下:
Function.prototype.myBind = function(quoteObj,...args){ // 新建一個變量 賦值為this,表示當前函數 const fn = this args = args ? args : [] // 返回一個newFn函數,在里面調用fn return function newFn(...newFnArgs) { // 新的函數被當作構造函數時,返回new fn if (this instanceof newFn) { return new fn(...args,...newFnArgs) } // 這里偷個懶,直接返回當前函數調用call方法,以實現this的改變 return fn.call(quoteObj,...args,...newFnArgs) } }