# 手写 call、apply、bind

# 1、call和apply概念

在 javascript 中,call和apply都是为了改变某个函数运行时的上下文(context)而存在的,换句话说,就是为了改变函数体内部 this 的指向。

func.call(this, arg1, arg2);
func.apply(this, [arg1, arg2])
1
2

其中 this 是你想指定的上下文,他可以是任何一个 JavaScript 对象(JavaScript 中一切皆对象),call 需要把参数按顺序传递进去,而 apply 则是把参数放在数组里。

# 2、apply、call实例

  • 数组之间追加
var array1 = [12 , "foo" , {name:"Joe"} , -2458]; 
var array2 = ["Doe" , 555 , 100]; 
Array.prototype.push.apply(array1, array2); 
// array1 值为  [12 , "foo" , {name:"Joe"} , -2458 , "Doe" , 555 , 100] 
1
2
3
4
  • 获取数组中的最大值和最小值
var numbers = [5, 458 , 120 , -215 ]; 
var maxInNumbers = Math.max.apply(Math, numbers),   //458
    maxInNumbers = Math.max.call(Math,5, 458 , 120 , -215); //458
1
2
3

number 本身没有 max 方法,但是 Math 有,我们就可以借助 call 或者 apply 使用其方法。

  • 验证是否是数组(前提是toString()方法没有被重写过)
function isArray(obj){ 
    return Object.prototype.toString.call(obj) === '[object Array]' ;
}
1
2
3
  • 类(伪)数组使用数组方法
var domNodes = Array.prototype.slice.call(document.getElementsByTagName("*"));
1

# 3、bind

bind()方法会创建一个新函数,称为绑定函数,当调用这个绑定函数时,绑定函数会以创建它时传入 bind()方法的第一个参数作为 this,使这个函数不论怎么调用都有同样的this值,传入 bind() 方法的第二个以及以后的参数加上绑定函数运行时本身的参数按照顺序作为原函数的参数来调用原函数。

# 作用

  • 绑定函数
this.num = 9; 
var mymodule = {
  num: 81,
  getNum: function() { 
    console.log(this.num);
  }
};

mymodule.getNum(); // 81

var getNum = mymodule.getNum;
getNum(); // 9, 因为在这个例子中,"this"指向全局对象

var boundGetNum = getNum.bind(mymodule);
boundGetNum(); // 81
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 4、三者比较

var obj = {
    x: 81,
};
 
var foo = {
    getX: function() {
        return this.x;
    }
}
 
console.log(foo.getX.bind(obj)());  //81
console.log(foo.getX.call(obj));    //81
console.log(foo.getX.apply(obj));   //81
1
2
3
4
5
6
7
8
9
10
11
12
13
  • apply 、 call 、bind 三者都是用来改变函数的this对象的指向的,第一个参数都是this要指向的对象,都可以利用后续参数传参。
  • bind不会立即调用,其他两个会立即调用,如果多次调用bind,那么第一次bind才会有效。

# 5、手写实现

Function.prototype.MyCall = function(context, ...args) {
	if (typeof this !== 'function') {
		throw new TypeError('Error')
	}
	context = context || window
	context.fn = this
	return context.fn(...args)
}
Function.prototype.MyApply = function(context, args) {
	if (typeof this !== 'function') {
		throw new TypeError('Error')
	}
	context = context || window
	context.fn = this
	return args ? context.fn(...args) : context.fn()
}
Function.prototype.MyBind = function(context) {
	if (typeof this !== 'function') {
		throw new TypeError('Error')
	}
	return (...args) => {
		context = context || window
		context.fn = this
		return args ? context.fn(...args) : context.fn()
	}
}

var obj = {
	x: 81,
};

var foo = {
	getX: function(y, z) {
		return this.x + y + z;
	}
}

console.log(foo.getX.MyBind(obj)(1, 2)); //84
console.log(foo.getX.MyCall(obj, 1, 2)); //84
console.log(foo.getX.MyApply(obj, [1, 2])); //84
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
38
39
40
Last Updated: 11/24/2020, 12:34:38 PM