稍微翻了一下call,apply, bind 的各類論壇上的文章, 發現講的都太淺了,大部分都只講了個用法, 對於實現的原理卻都沒有提,所以,在這裏,我寫下這篇文章, 但願能讓你們認識到原理所在。app
衆所周知, 這三個函數都是改變執行上下文的 , 那麼咱們來捋一捋,這些函數內部到底作了什麼。函數
Function是函數對象的構造方法,call,apply,bind 都是函數原型上的方法 做爲實例 他自身也有這三個方法this
圈中的爲原型方法, 方塊的爲實例方法,另外length屬性就是argument的長度,咱們一般調用一個函數spa
function a(){ console.log(this,'a')}; function b(b){console.log(b)} a.call(b,'我是B的參數')
執行a, 並把context指向b, 這裏你們都沒有疑問, 那麼問題來了prototype
function a(){ console.log(this,'a')}; function b(){console.log(this,'b')} a.call.call.call(b,'b') // 這個結果是什麼呢? 答案是
傻眼了吧 ? 怎麼執行了B 而且this指向了這個 b字符串code
咱們來分析一下 call是原型上的方法 那麼a.call 他自己也是一個函數 因此a.call.call.call 不就是a.call.call的原型上的call方法麼?
因此不就是執行call.call 並改變 call.call的上下文對象
咱們來擼一遍call的源碼,blog
第一個參數是上下文, 當咱們call(null),this指向了window 當咱們傳入字符串 會把字符串包裝成對象ip
a.call 執行 this是指向a的(誰調用this 指向誰) 而後又執行了a方法,因此內部是字符串
Function.prototype.call = function(context){ context = context ? Object(context):window this() // 由於調用的都是函數 因此this是一個函數 也就是a }
那這樣並未改變this指向啊,咋辦? 上句話不是說((誰調用this 指向誰)),因此咱們要在內部改變掉this,作以下修改
Function.prototype.call = function(context){ context = context ? Object(context):window context.fn = this context.fn() // 經過調用context.fn 來改變調用者 實現fn的this指向context 即改變a內部的this }
那麼參數怎麼傳呢,咱們首先要拿到call的裏的參數
Function.prototype.call = function(context,...args){ context = context ? Object(context):window context.fn = this let r = context.fn(...args) // 經過調用context.fn 來改變調用者 實現fn的this指向context 即改變a內部的this delete context.fn //刪除屬性 return r // 返回執行的結果 }
如今咱們回頭分析一下a.call.call.call(b,'b')
a.call.call是個函數,它調用call方法 執行它 咱們進入函數裏面看看
首先把context 也就是b轉爲字符串對象 它的屬性上賦予fn 也就是a.call.call ,而後執行context.fn(...args), 也就是a.call.call('b') 接着刪除fn 返回執行結果 宏觀來看 是a.call.call這個函數去執行並傳入('b') a.call.call 也就是Function.prototype.call 因此就是call('b') 因此啊, 結果纔是this是指向string的b 而且參數是undefined
Function.prototype.call = function(context,...args){ context = context ? Object(context):window context.fn = this //a.call.call('b') var r = context.fn(...args) // 經過調用context.fn 來改變調用者 實現fn的this指向context 即改變a內部的this delete context.fn return r } function a(){ console.log(this,'a')}; function b(args){console.log('我是this:' + this,'我是b的參數args:' + args)} a.call.call.call(b,'我究竟是參數呢仍是this')
結論!
一個函數call2次或者2次以上 執行的永遠是b(b須要是一個函數), 而且call的第二個參數成爲當前context
apply 就是參數不一樣 直接上代碼
Function.prototype.apply = function(context,...args){ context = context ? Object(context):window context.fn = this; var r; if(args.length){ r = context.fn(...args) delete context.fn }else{ r = context.fn() } return r } function a(args){ console.log(this,args)}; function b(){console.log('我是this:' + this)} a.apply(b,[1,2,3,4])
bind 明天寫 累了