“ 在学习和工作中,显式的改变this指向也是常遇到的,那么如何显式的改变this指向呢?聪明的你一定猜到啦,肯定是调用bind、call、apply这三种方法来改变。但是今天我并不会介绍这三个方法的使用,而是手动封装它们,做到知其所以然。”
1. bind方法封装
bind方法创建一个新的绑定函数,调用新绑定函数,会在指定的作用域中传入参数并执行。bind函数与call和apply函数不一样在于,它会返回一个新的绑定函数,当你再执行它的时候才会真正的执行。了解了之后我们就来看看bind如何封装的,代码如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| // 由于是函数调用,那么在函数原型上添加方法即可 Function.prototype.myBind = function (context) { // 首先判断绑定的是否是函数 if (typeof this !== 'function') { throw new TypeError('not function'); } const _this = this; const args = [...arguments].slice(1); // 这里不能使用箭头函数,因为箭头函数不能实例化、arguments return function F () { // 是否实例化 if (this instanceof F) { return new _this(...args, ...arguments); } return _this.apply(context, args.concat(...arguments)); } };
|
2. call方法封装
在上面写完bind函数封封装之后,接下来我们再来写一下call方法的封装,call方法就比bind方法更简单,因为调用它并不会像bind方法会返回一个函数,调用call方法一次就会执行。接下来就是看看call方法如何封装的,代码如下所示:
1 2 3 4 5 6 7 8 9 10 11 12
| Function.prototype.myCall = function (context = window) { if (typeof this !== 'function') { throw new TypeError('not function'); } // 将方法引用到指定对象中,对象的key为fn(作用:改变this指向) context.fn = this; const args = [...arguments].slice(1); // 执行函数,它的this指向context const result = context.fn(...args); delete context.fn; return result; }
|
3. apply方法封装
在02中写完了call方法的封装,接下来就来写一下apply方法的封装,它与call方法的不同之处在于传递的第二个参数不同,call要求传递的第二个参数以及第二个之后的参数值可以是任意类型,而apply传递的第二个参数是一个数组或者类数组。接下来就是看看apply方法如何封装的,代码如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| Function.prototype.myApply = function (context = window) { if (typeof this !== 'function') { throw new TypeError('not function'); } context.fn = this; let result; // 判断是否给apply第二个参数传递值 if (arguments[1]) { result = context.fn(arguments[1]); } else { result = context.fn(); } delete context.fn; return result; };
|