call、apply、bind这三个方法都可以显式的指定调用函数的this指向,那么这三个方法会有什么区别呢?本文将会进行详细讲解,通过本文你将学习到如何使用这三个方法,以及这三种方法的源码是怎么实现的
call()
和apply()
它们作用一模一样,都实现改变调用函数的this
指向并同时执行该函数,区别在于传入参数的形式的不同。
call
传入的参数不固定,第一个参数是this
指向,从第二个开始后面的每个参数都依次传入调用的函数。apply
只接受两个参数,第一个参数与call
相同,是this
指向,第二个参数为带下标的集合,这个集合可以是数组也可以是伪数组,apply
方法把这个集合的所有元素作为参数传递给调用的函数。bind()
方法不会执行函数,但是也能改变函数内部this指向,它的语法是:fu.bind(thisArg, arg1,arg2...)
,第一个参数是函数运行时指定的this的值,后面的参数都是函数运行时的传参,返回由指定this值和初始化采纳数改造的原函数的拷贝。
下面看bind()
的示例:
let o = {
name: 'andy'
}
function fn() {
console.log(this)
}
let f = fn.bind(o) // 此时函数f的this指向对象o
f() // {name: 'andy'}
// 返回的是原函数改变this之后产生的新函数
function fn(a, b) {
console.log(this)
console.log(a + b)
}
let f = fn.bind(o, 1, 2) // 传参
f() // {name: 'andy'} 3
下面总结一下
bind | apply | call | |
---|---|---|---|
是否执行调用的函数 | 否 | 是 | 是 |
参数 | (this指向,参1,参2…) | (this指向,[参数数组]) | (this指向,参1,参2…) |
用途 | 改变定时器内部的this指向等 | 跟数组有关系,比如借助于数学对象实现数组最大值最小值 | 经常用做继承 |
使用bind改变定时器指向,实现点击按钮后三秒后禁用按钮:
let btn = document.querySelector('button')
btn.onclick = function() {
this.disabled = true // 这个this指向btn
let that = this
setTimeout(function() {
// this.disabled = false // 定时器函数里面的this指向的是window
// that.disable = false 可行的方法
this.disabled = false
}.bind(this), 3000)
}
使用apply实现求数组最大值最小值:
let arr = [15, 6, 12, 13, 166666]
console.log(Math.max.apply(this, arr)) // 166666
console.log(Math.min.apply(this, arr)) // 6
call()
实现思路:
window
Function.prototype.myCall()
直接调用的判断this
方法(this
指向调用bind()
的方法)挂载到上下文对象上,需要用Symbol
声明一个对象属性result
后,删除挂载的方法result
Function.prototype.myCall = function (context = window, ...args) {
if (this == Function.prototype) {
return undefined; // 防止Function.prototype.myCall()直接调用
}
const fn = Symbol("fn");
// this指向的是要被执行的函数,也就是call的第一个参数
// 其实就是把this挂载到上下文对象上,然后执行它,执行完拿到返回结果后,删除挂载的函数
context[fn] = this;
const result = context[fn](...args);
delete context[fn];
return result;
};
// 示例
const obj = {
name: "zhangsan",
age: 18,
};
let fun = function (mes, sym) {
console.log(mes + sym, this.age + "岁的" + this.name);
};
fun.myCall(obj, "Hello", "!"); // Hello! 18岁的zhangsan
apply()
:更call()
的实现思路一模一样,只是参数变数组
Function.prototype.myApply = function (context = window, args = []) {
if (this == Function.prototype) {
return undefined; // 防止Function.prototype.myApply()直接调用
}
const fn = Symbol("fn");
context[fn] = this;
const result = context[fn](...args);
delete context[fn];
return result;
};
// 示例
const obj = {
name: "zhangsan",
age: 18,
};
let fun = function (mes, sym) {
console.log(mes + sym, this.age + "岁的" + this.name);
};
fun.myApply(obj, ["Hello", "!"]); // Hello! 18岁的zhangsan
bind()
:实现思路:
window
Function.prototype.myBind()
直接调用的判断原函数.apply()
方法Function.prototype.myBind = function (context = window, ...args) {
if (this == Function.prototype) {
return undefined; // 防止Function.prototype.myBind()直接调用
}
// 拿到原函数
const fn = this;
// 返回一个新的函数,函数的参数整合了原函数+新函数
return function (...newArgs) {
return fn.apply(context, args.concat(newArgs));
};
};
// 示例
const obj = {
name: "zhangsan",
age: 18,
};
let fun = function (mes, sym) {
console.log(mes + sym, this.age + "岁的" + this.name);
};
const f = fun.myBind(obj); // 返回新函数
f("Hello", "!"); // Hello! 18岁的zhangsan