call、apply、bind
# bind
先看使用方法
const obj1 = {
name: 'lili'
}
const obj2 = {
name: 'xiaoming'
}
window.name = 'window'
function who (text) {
console.log(`我是${this.name}, ${text}`)
}
who('我是工具') // 我是window,我是工具
const fn1 = who.bind(obj1)
fn1('我是女孩') // 我是lili,我是女孩
const fn2 = who.bind(obj2, '我是男孩')
fn2() // 我是xiaoming,我是男孩
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
由上看出几点:
- bind()函数执行,不会执行原函数,而是返回一个新函数,新函数执行时才会执行原函数
- bind()可以改变this的指向,第一次执行时,who为普通函数,this指向window,后两次输出分别指向obj1和obj2
- bind()接受多个参数,第一个参数为原函数执行过程中this的指向,从第二个参数开始,参数是可选的,可以在bind时传入,也可以在返回的新函数执行时传入
原函数参数及顺序,是bind时第二个参数开始的参数+新函数执行时传入的参数顺序,多传入的参数不生效。利用这个特性,==可以使一个函数拥有预设的初始参数==,看代码:
function list () {
return Array.prototype.slice.call(arguments)
}
const list1 = list(1, 2, 3) // [1, 2, 3]
const paramToList = list.bind(null, 5)
const lsit2 = paramToList(1, 2) // [5, 1, 2]
const list3 = paramToList() // [5]
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
这样,在bind时传入的参数5,就会作为paramToList函数的预设参数,在调用时总是出现在第一个参数位置。
# 实现bind
既然已经知道了 bind() 函数的作用,我们就可以通过它的作用,慢慢还原它的实现经过
首先,biind() 调用是 fun.bind(obj),直接由一个函数发起调用,且函数上没有这个方法,证明 bind() 存在于函数的原型链上,如下
Function.prototype.bind = function (obj) {
//
}
1
2
3
2
3
其次,bind() 函数执行不会执行原函数,会返回一个新函数,新函数执行时才会执行原函数,即,bind中返回的函数执行时,才会打印“执行”,如下
Function.prototype.bind = function (obj) {
// 保存原函数
// bind作为对象的方法调用,this指向调用方法的对象,即执行 func.bind(obj) 时,this 指向 func
const self = this
// 返回新函数
return function () {
// 需要在这里执行原函数
self()
}
}
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
实际使用一下看看,如下
Function.prototype.bind = function (obj) {
// 保存原函数
// bind作为对象的方法调用,this指向调用方法的对象,即执行 func.bind(obj) 时,this 指向 func
const self = this
// 返回新函数
return function () {
// 需要在这里执行原函数
self()
}
}
const person = {
legs: 2
}
function showLegs () {
console.log('执行')
console.log('legs', this.legs)
}
const func = showLegs.bind(person)
func() // 执行 // legs undefined
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
然后,bind() 返回了一个函数,函数再执行,执行的是原函数,但是原函数中的this并没有被绑定到person,要想将 this 绑定到 person,要在新函数,即 func 执行时做改变 this 指向的操作,this 由指向 func 改为指向 person,如下
Function.prototype.bind = function (obj) {
// 保存原函数
// bind作为对象的方法调用,this指向调用方法的对象,即执行 func.bind(obj) 时,this 指向 func
const self = this
// 返回新函数
return function () {
// 需要在这里执行原函数,并且改变this指向
self.apply(obj)
}
}
const person = {
legs: 2
}
function showLegs () {
console.log('legs', this.legs)
}
const func = showLegs.bind(person)
func()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
到这里,bind() 就成功实现了,但是我们还 bind() 可以接受多个参数,从第二个开始作为参数传入,而且返回的新函数中也可以传入参数,所以我们还需要将实现优化,支持传入参数,如下
// 最终版
Function.prototype.bind = function () {
// 保存原函数
// bind作为对象的方法调用,this指向调用方法的对象,即执行 func.bind(obj) 时,this 指向 func
const self = this
// 拆解参数
const obj = [].shift.call(arguments)
const args = [].slice.call(arguments)
// 返回新函数
return function () {
// 拆解参数
const allArgs = [].concat.call(args, [].slice.call(arguments))
// 需要在这里执行原函数,并且改变this指向
self.apply(obj, allArgs)
}
}
const person = {
legs: 2
}
function showLegs (name, endText) {
console.log('legs', this.legs)
console.log(`${name} has ${this.legs} legs, ${endText}`)
}
const func = showLegs.bind(person, 'lili')
func(' right') // lili has 2 legs, right
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
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
# 实现bind总结
上述过程中,我们由bind的作用推导出bind的实现,根据以上代码查看下面的一些要点,帮助我们更快的实现bind
- bind作用:返回一个新函数func,新函数func执行时执行被改变了this指向的原函数showLegs
- bind实现:返回新函数,在新函数中改变原函数指向
上次更新: 2024-01-12 19:09:34