5、手写 call、apply、bind

它们的作用都是改变 this 指向

call:fn.call(obj, args1, args2, args3...)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
Function.prototype.newCall = function(context, ...args) {
// 处理参数:this 指向的新对象,兜底为 window
context = context || window

// 当前 this 指向调用该方法的函数,即函数本身
const fn = this

// 声明唯一值,作为 新对象 的 key
const key = Symbol('context-key')
// 将函数本身赋值到新对象上
context[key] = fn
// 在新对象上调用函数,传入后续的参数,则实现了 this 指向的改变
// 因为函数是在对象上调用的,所以函数内的 this 将指向该对象
const result = context[key](...args)

// 删除刚刚赋值的 key,解绑
delete context[key]

// 返回函数执行的结果
return result

}

// 示例函数:
function fn(callName, callFnName) {
console.log(callName)
console.log(callFnName)
console.log(this.name)
}

// 示例调用:
fn.newCall({ name: '张三' }, '张三 call 的', 'newCall')

// 打印结果:
// '张三 call 的'
// 'newCall'
// '张三'

那对应的 apply,只需要更改下函数定义参数的写法
fn.apply(obj, [args1, args2, args3...])

1
2
3
4
5
6
7
8
9
10
11
Function.prototype.newApply = function(context, args) {
// 里面的内容完全不变
}

// 示例调用:第二个参数改为数组
fn.newApply({ name: '张三' }, ['张三 apply 的', 'newApply'])

// 打印结果:
// '张三 apply 的'
// 'newApply'
// '张三'

那对应的 bind 本质可以借助上面的call/apply实现
fn.bind(obj, args1, args2, args3...)()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
Function.prototype.newBind = function(context, ...args) {
// 处理参数:this 指向的新对象,兜底为 window
context = context || window

// 当前 this 指向调用该方法的函数,即函数本身
const fn = this

return function(..._args) {
return fn.newApply(context, args.concat(_args))
}
}

// 示例函数:
function fn(callName, callFnName) {
console.log(callName)
console.log(callFnName)
console.log(this.name)
}

// 示例调用:
const bindFn = fn.newBind({ name: '张三' }, '张三 bind 的', 'newBind')
bindFn()

// 打印结果:
// '张三 bind 的'
// 'newBind'
// '张三'

5、手写 call、apply、bind
https://mrhzq.github.io/职业上一二事/前端面试/每日一题/5、手写 call、apply、bind/
作者
黄智强
发布于
2024年1月23日
许可协议