目录
相信很多同学在面试中遇到这样的问题,今天我们就彻底搞懂JavaScript中的call、apply、 bind的用法、实现以及三者之间的区别,废话不多说,直接开撸!
一、函数用法
call,apply,bind 都是函数 Function 原型上的方法,三者的功能都是用来改变函数中的 this 指向。下面我们分别介绍下这三个函数用法。
1.call用法
call()
方法是预定义的 JavaScript 方法。它可以用来调用所有者对象作为参数的方法。通过 call()
,您能够使用属于另一个对象的方法。
案列:
const person = {
fullName: function () {
return this.firstName + this.lastName
}
}
const newPerson = {
firstName: "fu",
lastName: "chaoyang",
}
person.fullName.call(newPerson) // fuchaoyang
传参:
call传入的参数数量不固定,第一个参数代表函数内的this指向,从第二个参数开始往后,每个参数被依次传入函数。
const person = {
fullName: function (country, city) {
return this.firstName + this.lastName + " " + country + " " + city
}
}
const newPerson = {
firstName: "fu",
lastName: "chaoyang",
}
person.fullName.call(newPerson, 'china', 'xian') // fuchaoyang china xian
call是包装在apply上面的一颗语法糖,如果我们明确知道函数接受多少个参数,而且想一目了然的表达形参和实参的对应关系,那么我们可以用call来传达参数。
2.apply用法
apply接受两个参数,第一个参数指定了函数体内的this指向。第二个参数为一个带下标的集合,这个集合可以为数组,也可以为类数组,apply方法把这个集合中的元素作为参数传递给被调用的函数。
案列:
const person = {
fullName: function (country, city) {
return this.firstName + this.lastName + " " + country + " " + city
}
}
const newPerson = {
firstName: "fu",
lastName: "chaoyang",
}
person.fullName.apply(newPerson, ['china', 'xian']) // fuchaoyang china xian
这段代码中参数china,xian被放在数组中一起传给了person的fullName函数,分别对应fullName函数中的country,city参数。
当调用一个函数时,js的解释器并不会计较形参和实参在数量,类型以及顺序上的区别,js在内部就是用一个数组来表示的。从这个意义上来说,call比apply使用率更高,我们不必关心多少参数被传入函数,只要用apply一股脑推进去就行了。
3.bind用法
相信大家在使用React调用函数的时候必须使用bind(this),后直接在class中声明函数即可正常使用,但是为什么要使用这个呢?
bind()方法主要就是将函数绑定到某个对象,bind()会创建一个函数,函数体内的this对象的值会被绑定到传入bind()中的第一个参数的值,例如:f.bind(obj),实际上可以理解为obj.f(),这时f函数体内的this自然指向的是obj;
const person = {
fullName: function (country, city) {
return this.firstName + this.lastName + " " + country + " " + city
}
}
const newPerson = {
firstName: "fu",
lastName: "chaoyang",
}
person.fullName.bind(newPerson, 'china', 'xian') // 打印出fullName函数
person.fullName.bind(newPerson, 'china', 'xian')() // fuchaoyang china xian
bind传参和call是一致的,内部实现是先把当前函数保存起来,然后返回一个新的函数,当我们将来要执行当前函数时,实际返回的是刚刚返回的新的fullName函数。它不会立即执行,而是需要的时候调用即可。
二、手写源码实现
1. 手写call函数
// call实现
Function.prototype.myCall = function (context) {
context = context || window
const arg = [...arguments].slice(1)
const fn = Symbol()
context[fn] = this
const result = context[fn](...arg)
delete context[fn]
return result
}
2.手写apply函数
// apply 实现
Function.prototype.myApply = function (context, args) {
context = context || window // 默认 window
args = [...args] // 参数
const fn = Symbol() // 给 context 设置一个独一无二的属性,避免覆盖原有属性
context[fn] = this // 这里的 this 指向调用它的函数fn
const result = context[fn](...args) // 调用之
delete context[fn] // 删除添加的属性
return result // 返回值
}
3.手写bind函数
// bind 实现
Function.prototype.myBind = function (context) {
context = context || window // 默认 window
const args = [...arguments].slice(1) // 参数
const fn = this // 这里的 this 指向调用它的函数 fn
return function () {
return fn.apply(context, args)
}
}
三、区别以及使用场景
1.相同点
- bind、call、apply都是用来指定一个函数内部的this的值。
- 接收的第一个参数都是this要指向的对象。
- 都可以利用后续参数传参。
2.不同点
- call和bind传参相同,多个参数依次传入的。
- apply只有两个参数,第二个参数为数组。
- call和apply都是对函数进行直接调用,而bind方法不会立即调用函数,而是返回一个修改this后的函数。
3.使用场景
- call函数的使用多用于类的继承。
- apply函数可配合Math.max()用于计算数组最大值等。
- bind函数可用于函数内部有定时器,改变定时器内部的this指向。
欢迎在评论区交流。如果文章对你有所帮助,❤️关注+点赞❤️鼓励一下!博主会持续更新。。。。
往期回顾
vue3.x使用prerender-spa-plugin预渲染达到SEO优化
vue3.x使用prerender-spa-plugin预渲染达到SEO优化