前言
javascript
中 call / apply / bind
这三个方法是日常工作中比较实用的改变函数 this
指向的方法,很方便,但由于涉及到对 this
的理解,同时也是很容易出错的方法,也是面试官喜欢问的考题之一,今天就和大家一起好好聊一聊这三个方法
call / apply / bind 的区别
我们先简单的过一下他们之间的异同点,有个简单概念之后再详细分析
共同点
他们的功能一致,都是用来改变函数的 this
指向
// 语法 函数.call(thisArg, arg1, arg2, ...) 函数.apply(thisArg, [argsArray]) 函数.bind(thisArg, [argsArray]) 复制代码
不同点
- call / apply 可以立即执行;bind 不会立即执行,而是返回一个函数,可以在需要的时候再执行
- 参数不同:apply 第二个参数是数组;call 和 bind 有多个参数需要用逗号隔开挨个写
简单应用场景
- 改写
this
指向,让目标对象使用一些方法
const person = { name: '江', say: function () { console.log(this.name) } } person.say() // 江 const obj = { name: '李' } person.say.call(obj) // 李 person.say.apply(obj) // 李 person.say.bind(obj)() // 李 复制代码
- 借用方法
const numbers = [5, 458 , 120 , -215 ]; const maxInNumbers = Math.max.apply(Math, numbers), //458 maxInNumbers = Math.max.call(Math,5, 458 , 120 , -215); //458 复制代码
number 本身没有 max 方法,但是 Math 有,我们就可以借助 call 或者 apply 使用其方法
手写 call
Function.prototype.myCall = function (thisArg, ...argsArray) { // 判断 thisArg 是否 null / undefine if (thisArg === null || thisArg === undefined) { thisArg = window } console.log(this) // 这里的 this 是调用的函数 thisArg.fn = this thisArg.fn(...argsArray) } 复制代码
手写 apply
Function.prototype.myApply = function (thisArg, argsArray) { // 判断 thisArg 是否 null / undefine if (thisArg === null || thisArg === undefined) { thisArg = window } console.log(this) // 这里的 this 是调用的函数 thisArg.fn = this thisArg.fn(...argsArray) } 复制代码
手写 bind
Function.prototype.myBind = function (thisArg, argsArray) { // 判断 thisArg 是否 null / undefine if (thisArg === null || thisArg === undefined) { thisArg = window } console.log(this) // 这里的 this 是调用的函数 thisArg.fn = this return function () { thisArg.fn(...argsArray) } } 复制代码
使用一下我们手写的三个方法,看是否满足我们的要求
const person = { name: '江', say: function (word) { console.log(this.name + ":" + word) } } person.say('你好') // 江:你好 const obj = { name: '李' } person.say.myCall(obj, 'hello') // 李:hello person.say.myCall(obj, ['hello']) // 李:hello person.say.myBind(obj, ['hello'])() // 李:hello 复制代码
可以看到可以按期望输出结果了~
总结
对于改写 this
指向的实现,关键点是要理解:
- 我们自定义的方法,必须是挂载在
Function
这个个构造函数上的,这是一个大前提,因为只有这样,我们在任意方法上使用.
才能找到我们的方法属性(其实就是原型链) - 还有就是对当前函数里
this
的理解,这个场景可以简单理解为this
是谁调用就指向谁,那么函数.myCall()
就很清晰了,是函数调用了myCall
,所以myCall
方法里的this
指向的就是函数 - 理解了以上两点,就只要把
this(想执行的方法)
挂到我们的目标对象thisArg
上面就可以了