文章介绍了 JavaScript 中 apply、call 和 bind 方法,apply 以数组形式传参,call 以参数列表传参,两者用于函数调用时绑定 this 值,bind 会返回新函数并处理参数合并。还讲解了这三个方法的自定义实现及测试示例。
关联问题: apply和call区别在哪 bind如何合并参数 怎样实现bind方法
1.方法介绍
apply、call和bind都是系统提供给我们的内置方法,每个函数都可以使用这三种方法,是因为apply、call和bind都实现在了Function的原型上(Function.prototype),而他们的作用都是给我们函数调用时显式绑定上this。下面先介绍一下它们的基本用法:
-
apply方法:调用一个具有给定this值的函数,以及以一个数组(或类数组对象) 的形式提供的参数。
-
使用语法:
func.apply(thisArg, [argsArray])
- thisArg:在func函数调用时绑定的this值;
- [argsArray]:一个数组或者类数组对象,其中的数组元素将作为单独的参数传给func函数;
-
使用效果:
1function foo(x, y ,z) { 2 console.log(this, x, y, z) 3} 4 5const obj = { name: 'curry', age: 30 } 6 7 * 1.将obj对象绑定给foo函数的this 8 * 2.数组中的1 2 3分别传递给foo函数对应的三个参数 9 */ 10foo.apply(obj, [1, 2, 3])
-
-
call方法:使用一个指定的 this值和单独给出的一个或多个参数来调用一个函数。
-
使用语法:
func.call(thisArg, arg1, arg2, ...)
- thisArg:在func函数调用时绑定的this值;
- arg1, arg2, …:指定的参数列表,将作为参数传递给func函数;
-
使用效果:
1function foo(x, y ,z) { 2 console.log(this, x, y, z) 3} 4 5const obj = { name: 'curry', age: 30 } 6 7 * 1.将obj对象绑定给foo函数的this 8 * 2.call剩余参数中的a b c分别传递给foo函数对应的三个参数 9 */ 10foo.call(obj, 'a', 'b', 'c')
-
-
bind方法:创建一个新的函数,在bind()被调用时,这个新函数的this被指定为bind()的第一个参数,而其余参数将作为新函数的参数,供调用时使用。
-
使用语法:
func.bind(thisArg[, arg1[, arg2[, ...]]])
- thisArg:调用func函数时作为this参数传递给目标函数的值;
- arg1, arg2, …:当目标函数被调用时,被预置入func函数的参数列表中的参数;
-
使用效果: function foo(…args) { console.log(this, …args) }
1const obj = { name: 'curry', age: 30 } 2 3 * 1.将obj对象绑定给foo函数的this 4 * 2.bind剩余参数中的1 2 3分别传递给foo函数中参数 5 * 3.也可在newFoo调用时传入参数,这时bind传递的参数会与newFoo调用时传递的参数进行合并 6 */ 7const newFoo = foo.bind(obj, 1, 2, 3) 8newFoo() 9newFoo('a', 'b', 'c')
-
总结:
- apply和call主要用于在函数调用时给函数的this绑定对应的值,两者作用类似,主要区别就是除了第一个参数,apply方法接受的是一个参数数组,而call方法接受的是参数列表。
- bind也是给函数指定this所绑定的值,不同于apply和call的是,它会返回一个新的函数,新函数中的this指向就是我们所指定的值,且分别传入的参数会进行合并。
2.apply、call和bind方法的实现
为了所有定义的函数能够使用我们自定义的apply、call和bind方法,所以需要将自己实现的方法挂在Function的原型上,这样所有的函数就可以通过原型链找到自定义的这三个方法了。
2.1.apply的实现
1 Function.prototype.myCall = function(thisArg, ...args) {
2
3 const fn = this;
4
5
6 thisArg = (thisArg === null || thisArg === undefined) ? window : Object(thisArg);
7
8
9
10 const fnSymbol = Symbol();
11
12 Object.defineProperty(thisArg,fnSymbol,{
13 enumerable: false,
14 configurable: false,
15 writable: false,
16
17
18 value : this
19 })
20
21
22
23
24
25 args = args || []
26
27
28
29
30
31
32
33
34
35
36 return result
37
38 }
测试:虽然打印出来的对象中还存在Symbol属性,实际上已经通过delete
删除了,这里是对象引用的问题。
1function foo(x, y, z) {
2 console.log(this, x, y, z)
3}
4
5foo.myApply({name: 'curry'}, [1, 2, 3])
2.2.call的实现
call方法的实现和apply方法的实现差不多,主要在于后面参数的处理。
1Function.prototype.myApply = function (thisArg, args) {
2 const fn = this
3 thisArg = (thisArg === null || thisArg === undefined) ? window : Object(thisArg)
4 const fnSymbol = Symbol()
5 Object.defineProperty(thisArg, fnSymbol, {
6 enumerable: false,
7 configurable: false,
8 writable: false,
9 value: fn
10 })
11 const result = thisArg[fnSymbol](...args)
12 delete thisArg[fnSymbol]
13 return result
14 }
15
测试:
1function foo(x, y, z) {
2 console.log(this, x, y, z)
3}
4
5foo.myCall({name: 'curry'}, 1, 2, 3)
2.3.bind的实现
bind方法的实现稍微复杂一点,需要考虑到参数合并的问题,以及返回一个新对象
1Function.prototype.myBind = function(thisArg, ...argsArray) {
2
3 const fn = this
4
5
6 thisArg = (thisArg === null || thisArg === undefined) ? window : Object(thisArg)
7
8
9 const fnSymbol = Symbol();
10 Object.defineProperty(thisArg,fnSymbol,{
11 enumerable: false,
12 configurable: false,
13 writable: false,
14 value:this
15 })
16
17
18
19
20
21 function newFn(...args) {
22
23 const allArgs = [...argsArray, ...args]
24
25 const result = thisArg[fnSymbol](...allArgs)
26
27 delete thisArg[fnSymbol]
28
29
30 return result
31 }
32
33
34 return newFn
35}
测试:
1function foo(x, y, z) {
2 console.log(this, x, y, z)
3}
4
5const newFoo = foo.myBind({ name: 'curry' }, 1, 2)
6newFoo(3)