call,apply,bind的内部原理实现
call
call 方法使用一个函数执行的时候更改本身 this 指向,并传入一个或者多个参数。
var obj = { name: '$call' } function _fun() { console.log(this.name, ...arguments) } _fun.call(obj, 'call1', 'call2', 'call3') // $call call1 call2 call3
内部实现原理:
Function.prototype.$$call = function (context) { // 第一个参数为 this 指向值,如无则指向 window context = context || window // 将本身的函数保存下来,在后面需要执行,这一步 this 的指向已经指向了 context context.fn = this // 将后面传入的参数转为数组,取除第一个 this 指向剩下的所有参数 let args = [...arguments].slice(1) // 执行函数本身,并将参数传入 let result = context.fn(...args) // 销毁函数,避免作用域污染 delete context.fn return result }
apply
apply 方法同 call 一样使用一个函数执行的时候更改本身 this 指向,只是传参的时候只有一个,并且必须是数组(如果call与apply传参类型记不清,可以根据方法的第一个字母来区分,apply -> a(首字母) -> array)。
var obj = { name: '$call' } function _fun() { console.log(this.name, ...arguments) } _fun.apply(obj, ['call1', 'call2', 'call3']) // $call call1 call2 call3
内部实现原理:
Function.prototype.$$apply = function (context) { // 第一个参数为 this 指向值,如无则指向 window context = context || window // 将本身的函数保存下来,在后面需要执行,这一步 this 的指向已经指向了 context context.fn = this // 将后面传入的参数转为数组,取除第二个参数 let args = [...arguments][1] // 如果第二个参数不是对象则报错 if (typeof args !== 'object') { throw Error('CreateListFromArrayLike called on non-object') return } // 执行函数本身,并将参数传入 let result = context.fn(...args) // 销毁函数,避免作用域污染 delete context.fn return result }
bind
bind 方法与 call 和 apply 不同的点是后续的参数没有要求,但是 bind 会返回一个 this 指向已改变的函数,相同的是第一个参数就是 this 指向值
var obj = { name: '$call' } function _fun() { console.log(this.name, ...arguments) } _fun.call(obj, ['call1'], 'call3')('call2') // $call ["call1"] call3 call2
内部实现原理:
Function.prototype.$$bind = function (context) { // 取bind执行的时候除第一个后续的所有参数 let args = [...arguments].slice(1) // 函数本身缓存 let _this = this // 返回函数 return function () { // 合并返回函数执行传入的参数 let bindArg = [...args, ...arguments] // 再次调用时的函数本身执行 return _this.call(context, ...bindArg) } }
每一次的记录,都是向前迈进的一步