來看下 call 的原生表現形式:es6
var foo = { value: 1, } function bar() { console.log(this.value) } bar.call(foo) // 1
從上面代碼的執行結果,咱們能夠看到,call 首先改變了 this 的指向,使函數的 this 指向了 foo,而後使 bar 函數執行了。數組
總結以下:app
思考一下:咱們如何實現上面的效果呢?代碼改造以下:函數
//將bar函數掛載到foo對象上,使其成爲foo的方法,用foo.bar來調用測試
var foo = { value: 1, bar: function () { console.log(this.value) }, } foo.bar() //1
爲了模擬 call 方法,咱們能夠這樣作:this
代碼以下:prototype
Function.prototype.myCall = function (context) { const fn = Symbol('fn') // 聲明一個獨有的Symbol屬性, 防止fn覆蓋已有屬性 context = context || window // 若沒有傳入this, 默認綁定window對象 context.fn = this // 將函數掛載到對象的fn屬性上 const args = [...arguments].slice(1) // 處理傳入的參數 const result = context.fn(...args) // 經過對象的屬性調用該方法 delete context.fn // 刪除該屬性 return result } // 測試 function test(arg1, arg2) { console.log(arg1, arg2) console.log(this.a, this.b) } test.myCall( { a: 'a', b: 'b', }, 1, 2 )
咱們看一下上面的代碼:code
apply 和 call 實現相似,只是傳入的參數形式是數組形式,而不是逗號分隔的參數序列。對象
所以,藉助 es6 提供的...運算符,就能夠很方便的實現數組和參數序列的轉化。io
Function.prototype.myApply = function (context) { const fn = Symbol('fn') // 聲明一個獨有的Symbol屬性, 防止fn覆蓋已有屬性 context = context || window // 若沒有傳入this, 默認綁定window對象 context.fn = this // 將函數掛載到對象的fn屬性上 const args = [...arguments].slice(1) // 處理傳入的參數 const result = context.fn(args) // 經過對象的屬性調用該方法 delete context.fn // 刪除該屬性 return result } // 測試 function test(arr) { console.log(arr) console.log(this.a, this.b) } test.myApply( { a: 'a', b: 'b', }, [1, 2] )
在模擬 bind 的實現以前,先看一下 bind 的使用案例:
var obj = { a: 1 } function bar() { console.log(this.a) } bar.bind(obj)() //here
bind 函數雖然也能改變 bar 函數的 this,可是改變後,函數並不會執行,只是返回一個新的函數,想執行就得繼續調用,仔細觀察第五行代碼的寫法。
根據上面的使用案例,咱們先實現一個簡單版本的 bind:
Function.prototype.myBind = function (context) { return () => { // 要用箭頭函數,不然 this 指向錯誤 return this.call(context) } } var obj = { a: 1 } function bar() { console.log(this.a) } bar.myBind(obj)()
可是這樣比較簡陋,函數的參數一多就不能處理了,以下面這種狀況:
bar.bind(obj, 2)(2) // or bar.bind(obj)(2, 2)
爲了兼容 bind 調用時知足參數傳遞的不一樣方式,代碼修改以下:
Function.prototype.myBind = function (ctx, ...arg1) { return (...arg2) => { return this.call(ctx, ...arg1, ...arg2) } } //測試代碼 var obj = { a: 1 } function bar(b, c) { console.log(this.a + b + c) } bar.myBind(obj)(20, 30) bar.myBind(obj, 20, 30)()