正文
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)
}
}