目录
知识储备:
一:手写call方法
1、思路
2、实现
3、Symbol调优
二:手写apply方法
1、思路
2、实现
三:手写bind方法
1、思路
2、实现
四:总结
知识储备:
所有的方法都可以调用我们手写的方法,因此需要挂载在原型上,因此我们使用以下代码进行挂载
Function.prototype.myCall = function () { console.log(' 方法执行 ') }
一:手写call方法
call方法是改变this指向的一个常用方法,其写法是 func.call(thisArg,att1,att2,...) 。在这里第一个参数是我们要将this改变到哪个实例上,从第二个属性开始,就是我们要传递的参数。因为是直接传递的,所以我们这里不确定有几个参数,因此在重写的时候需要使用 ...args 来进行接收。同时在使用这个参数的时候需要 ...args 来进行结构。下面我们来整理一下思路以及实现吧。
1、思路
- 定义myCall方法
- 设置this并调用原函数
- 接收剩余参数并返回结果
2、实现
<script>
// 1. 定义myCall方法
Function.prototype.myCall = function (thisArg,...args) {
// 2. 设置this为原函数
// thisArg 传入的设置为this的对象
// 这里的this就是原函数 因为是 原函数.myCall()
thisArg.f = this
// 3. 接受剩余参数并返回结果
const res = thisArg.f(...args)
// 因为添加了f属性所以要删掉
delete thisArg.f
return res
}
// ------------- 测试代码 ----------------
const person = {
name : '张三'
}
function func(numA,numB){
console.log(this);
console.log(numA,numB);
return numA + numB
}
const res = func.myCall(person, 1, 2)
console.log('返回的值为:' + res);
</script>
3、Symbol调优
在上面的代码中,我们可以看到,是使用的 thisArg.f 指向了 this 。但是这样有一个弊端,即你无法确定在 thisArg 中是否存在 f 方法,因此我们使用Symbol进行调优。将myCall()中的代码替换如下:
//Symbol调优
// 2. 设置this为原函数
// thisArg 传入的设置为this的对象
// 这里的this就是原函数 因为是 原函数.myCall()
// 4. Symbol调优
const key = Symbol('key')
//这里是动态解析key
thisArg[key] = this
// 3. 接受剩余参数并返回结果
const res = thisArg[key](...args)
// 因为添加了f属性所以要删掉
delete thisArg[key]
return res
这里就不附加运行图了,最终的效果也是一样的哦!而且更为的安全可靠,推荐使用这种方法。
二:手写apply方法
apply方法是另一种比较常见的改变this指向的方法,这个方法和call方法一样,都是在调用时改变this的指向,但是 apply 与 call 不同的地方在于,apply接收的第一个参数是thisArg(需要指向的对象),而第二个参数则是一个数组。apply只有这两个参数,这一点是区别于 call 方法的。其余的地方基本上是一样的。下面是实现思路以及具体代码。
1、思路
- 定义myApply方法
- 设置this并调用原函数
- 接收参数并返回结果
2、实现
这个实现方法其实和myCall的实现方法很相近,不同的是接收参数的时候因为已经传的是数组了,所以我们不需要使用 ... 来接收不确定数量的参数,直接使用args就可以了。
<script>
// 1. 定义myCall方法
Function.prototype.myApply = function (thisArg,args) {
// 2. 设置this为原函数 直接使用Symbol调优
// thisArg 传入的设置为this的对象
// 这里的this就是原函数 因为是 原函数.myCall()
// 4. Symbol调优
const key = Symbol('key')
//这里是动态解析key
thisArg[key] = this
// 3. 接受剩余参数并返回结果
const res = thisArg[key](...args)
// 因为添加了f属性所以要删掉
delete thisArg[key]
return res
}
// ------------- 测试代码 ----------------
const person = {
name : '张三'
}
function func(numA,numB){
console.log(this);
console.log(numA,numB);
return numA + numB
}
const res = func.myApply(person, [1, 2])
console.log('返回的值为:' + res);
</script>
三:手写bind方法
bind 方法是直接区别于 call 方法和 apply 方法的,该方法是在创建时就改变了this的指定,同时返回的是一个新的方法,在调用新方法的时候同样可以传参,这样会和之前方法的参数进行一个拼接合并。该方法不会改变原方法的this指向,具体思路以及实现见下。
1、思路
- 定义myBind方法
- 返回绑定this的新函数
- 合并绑定和新传入的参数
2、实现
<script>
// 1. 定义myBind方法
Function.prototype.myBind = function (thisArg,...args) {
// 2. 返回绑定this的新函数
return (...reArgs) => {
// this :原函数(原函数.myBind)
//3. 合并绑定和新传入的参数,注意先后顺序
return this.call(thisArg,...args,...reArgs)
}
}
// ------------- 测试代码 ----------------
const person = {
name : '张三'
}
function func(numA,numB,numC,numD){
console.log(this);
console.log(numA,numB,numC,numD);
return numA + numB + numC + numD
}
const bindFunc = func.myBind(person, 1, 2)
const res = bindFunc(3,4)
console.log('返回的值为:' + res);
</script>
四:总结
call、apply、bind三个方法是改变this指向的重要方法,熟练的使用以及掌握其源码实现原理,能够帮助我们更好地理解和使用这三个方法。同时可以让我们在项目开发过程中规避掉很多不应该的错误,并且提高我们的逻辑思维能力。
因此,学习这三个方法是很有必要性的。希望本文能够对各位小伙伴有所帮助哦~