call apply bind的 區別及實現


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){
 如果第一個參數沒有傳值,默認指向window
let quote = quoteObj || window;
新建一個唯一symbol變量,避免變量重復
let func = Symbol()
例如:使用時是print.myCall(originPerson),所以這里的this指向 print
print定義在quote.fun上(為了能以對象調用形式綁定this)
quote[func] = this;
在此處看看quote是什么樣的:
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 : []
以對象調用形式調用func
const res = args.length > 0 ? quote[func](...args) : quote[func]()
刪除該方法,不然會對quoteObj造成污染(添加了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)
                }
            }

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM