稍微翻了一下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 明天写 累了