摘要:众所周知,这三个函数都是改变执行上下文的,那么我们来捋一捋,这些函数内部到底做了什么。
前言
稍微翻了一下call,apply, bind 的各种论坛上的文章, 发现讲的都太浅了,大部分都只讲了个用法, 对于实现的原理却都没有提,因此,在这里,我写下这篇文章, 希望能让大家认识到原理所在。
众所周知, 这三个函数都是改变执行上下文的 , 那么我们来捋一捋,这些函数内部到底做了什么。
callFunction是函数对象的构造方法,call,apply,bind 都是函数原型上的方法 作为实例 他自身也有这三个方法
圈中的为原型方法, 方块的为实例方法,另外length属性就是argument的长度,我们通常调用一个函数
function a(){ console.log(this,"a")}; function b(b){console.log(b)} a.call(b,"我是B的参数")
执行a, 并把context指向b, 这里大家都没有疑问, 那么问题来了
function a(){ console.log(this,"a")}; function b(){console.log(this,"b")} a.call.call.call(b,"b") // 这个结果是什么呢? 答案是
傻眼了吧 ? 怎么执行了B 并且this指向了这个 b字符串
我们来分析一下 call是原型上的方法 那么a.call 他本身也是一个函数 所以a.call.call.call 不就是a.call.call的原型上的call方法么?
所以不就是执行call.call 并改变 call.call的上下文
我们来撸一遍call的源码,
第一个参数是上下文, 当我们call(null),this指向了window 当我们传入字符串 会把字符串包装成对象
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 明天写 累了
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/101838.html
摘要:也就是说当返回的函数作为构造函数的时候,时指定的值会失效,但传入的参数依然生效。构造函数效果的优化实现但是在这个写法中,我们直接将,我们直接修改的时候,也会直接修改函数的。 JavaScript深入系列第十一篇,通过bind函数的模拟实现,带大家真正了解bind的特性 bind 一句话介绍 bind: bind() 方法会创建一个新函数。当这个新函数被调用时,bind() 的第一个参数...
摘要:和概览我们要将归为一类,单独归为一类三者的共同点是都可以指定和都是绑定在的原型上的,所以的实例都可以调用这三个方法至于为什么,看完这篇文章你就懂了如果你不懂什么是实例的话,请移步深入浅出面向对象和原型概念篇深入浅出面向对象和原型概念篇第一个 1.call/apply和bind概览 我们要将call/apply归为一类,bind单独归为一类 三者的共同点是都可以指定this call/...
摘要:三个方法的作用,都是改变的指向,只是用法稍微有些区别什么是既不指向函数自身,也不指函数的词法作用域。它在函数定义的时候是确定不了的在函数被调用时才发生的绑定,也就是说具体指向什么,取决于你是怎么调用的函数。 1.排序法 思路:给数组先排序(由大到小排序),第一项就是最大值 let arr = [1,5,6,7,9,20,40,2,3]; let max1 = arr.sort(func...
摘要:文章尽量使用大量实例进行讲解,它们的使用场景。在严格模式下,函数被调用后,里面的默认是后面通过调用函数上的和方法,该变指向,函数里面的指向。利用,可以传入外层的上下文。同样适用的还有,里面的对象,它也是一种类数组对象。 call,apply and bind in JavaScript 在ECMAScript中,每个函数都包含两个继承而来的方法:apply() 和 call(),这两个...
摘要:深入系列第十篇,通过和的模拟实现,带你揭开和改变的真相一句话介绍方法在使用一个指定的值和若干个指定的参数值的前提下调用某个函数或方法。如果有错误或者不严谨的地方,请务必给予指正,十分感谢。 JavaScript深入系列第十篇,通过call和apply的模拟实现,带你揭开call和apply改变this的真相 call 一句话介绍 call: call() 方法在使用一个指定的 this...
阅读 1875·2021-11-24 10:45
阅读 1414·2021-11-18 13:15
阅读 4433·2021-09-22 15:47
阅读 3788·2021-09-09 11:36
阅读 1978·2019-08-30 15:44
阅读 3048·2019-08-29 13:05
阅读 2468·2019-08-29 12:54
阅读 1949·2019-08-26 13:47