一、前言
在我们学习JavaScript中,不可避免的会遇到call、apply和bind的用法,三者的相似之处?三者的区别?
三者原生实现的方法?这些是本文主要探讨的内容。
二、call、apply和bind相似之处
1、对三者的定义:
call()方法调用一个函数,其具有一个指定的this值和分别地提供的参数(参数的列表)。
apply()方法调用一个函数,其具有一个指定的this值,以及作为一个数组(或类数组对象)提供的参数。
bind()其具有一个指定的this值和提供的参数, 返回一个原函数的拷贝
2、三者相似之处
* 都是用来改变函数的this对象的指向的。
* 第一个参数都是this要指向的对象。
* 都可以利用后续参数传参。
三、call、apply和bind的区别
1、举例说明:
var x = 0;
function f(y,z) {
console.log(this.x + y+ z)
}
var obj = {
x: 1
}
f(2,3); // 0+2+3=5
f.call(obj, 2,3); // 1+2+3=6
f.apply(obj, [2,3]) // 1+2+3=6
f.bind(obj,2,3)() // 1+2+3=6
- f() 直接调用返回的是5 因为直接调用this指向全局对象window,在全局对象中有一个x=0;
- f.call(obj,2,3)、f.apply(obj,[2,3]) 第一个参数obj,此时this指向的对象是obj,所以this.x = 1(obj.x=1),两者的主要区别在于传递的参数方式不同,call是已列表的方式传参,apply必须是数组的方式
- f.bind(obj,2,3) 如果后边不加上() 返回的只是f这个函数,加上()后才可调用
四、原生实现的方法
call 实现方法
实现思路:
- 不传入第一个参数,那么默认为 window
- 改变了 this 指向,让新的对象可以执行该函数。那么思路是否可以变成给新的对象添加一个函数,然后在执行完以后删除?
Function.prototype.myCall = function (context) {
// ① 如果myCall第一个参数null,此时context是window对象,如果传参就是这个对象,测试中context就是obj这个对象
var context = context || window;
// ② 第二给context这个对象添加一个方法,让这个方法等于this,this的指向问题:谁调用this就指向谁,此时this是f这个方法
// ③ 现在context数据如下
/*
context = {
x: 1,
fn: function f(y,z) {
console.log(this.x + y+ z)
}
}
*/
context.fn = this
// ④ 截取参数,此时args = [2,3]
var args = [...arguments].slice(1)
// ⑤ 调用context对象中fn方法,也就是测试中f的方法
var result = context.fn(...args)
// ⑥ 删除 fn
delete context.fn
return result
}
// 测试
var x = 0;
function f(y,z) {
console.log(this.x + y+ z)
}
var obj = {
x: 1
}
f.myCall(obj, 2,3); // 1+2+3=6
apply 实现方法
- apply 实现思路和call差不多,只是在处理传入第二参数不同
Function.prototype.myApply = function (context) {
var context = context || window
context.fn = this
var result
// 需要判断是否存储第二个参数
// 如果存在,就将第二个参数展开
if (arguments[1]) {
result = context.fn(...arguments[1])
} else {
result = context.fn()
}
delete context.fn
return result
}
bind 实现方法
Function.prototype.myBind = function (context) {
if (typeof this !== 'function') {
throw new TypeError('Error')
}
var _this = this
var args = [...arguments].slice(1)
// 返回一个函数
return function F() {
// 因为返回了一个函数,我们可以 new F(),所以需要判断
if (this instanceof F) {
return new _this(...args, ...arguments)
}
return _this.apply(context, args.concat(...arguments))
}
}