2022-09-13【手动实现改变this指向的方法】

71 阅读3分钟

js改变this指向的方法有三种,分别为call、apply和bind方法,我们来手动实现一下它们。 首先定义一个基础对象和函数,用来演示上述方法。

 let obj = {
            name: 'obj'
        }
 let fn = function (method, a, b) {
            console.log(`使用了${method}--名字是${this.name}---结果是${a + b}`)
        }

call方法的使用

call()方法使用一个指定的this值和单独给出的一个或多个参数来调用一个函数。 使用如下

 fn.call(obj, 'call', 1, 2)// 使用了call--名字是obj---结果是3

手写myCall方法

  • js的this指向调用者,谁调用函数,this指向谁
  • 如果构造出obj.fn,那么fn的this就指向了obj 基于以上分析,我们来实现一下myCall方法
 Function.prototype.myCall = function (target = window, ...args) {
            let self = this//这里的this即fn
            target.fn = self; //相当于在obj对象上增加了一个值为fn的属性
            target.fn(...args)//即obj调用了fn, fn指向了obj
            delete target['fn']//删除该属性
            return target
        }

执行下myCall方法,结果如下

 fn.myCall(obj, 'myCall', 1, 2); // 使用了myCall--名字是obj---结果是3

apply的使用

apply()方法调用一个具有给定this值的函数,以及一个数组(或一个类数组对象)的形式提供的参数。使用如下

fn.apply(obj, ['apply', 1, 2]); // 使用了apply--名字是obj---结果是3

手写myApply方法

myApply 和myCall只是传参不同,其余一样。

 Function.prototype.myApply = function (target = window, args) {
            let self = this//指向fn
            target.fn = self;
            target.fn(...args)//target调用了fn, fn指向了target
            delete target['fn']//删除该属性
            return target
        }

执行myApply方法,结果如下

fn.myApply(obj, ['myApply', 1, 2]); //使用了myApply--名字是obj---结果是3

bind方法的使用

bind()方法创建一个新的函数,在bind()被调用时,这个新函数的this被指定为bind()的第一个参数,而其余参数将作为新函数的参数,供调用使用。

  let bind1 = fn.bind(obj, 'bind1', 1, 2); // 一次性传参
  let bind2 = fn.bind(obj, 'bind2', 1);
      bind1(); //使用了bind1--名字是obj---结果是3
      bind2(2); // 使用了bind2--名字是obj---结果是3

手写myBind

  • bind函数需要返回一个函数,并且涉及到参数合并fn.bind(obj, 'bind2', 1)
  • 返回的函数涉及到直接调用和new的方式调用 基于以上分析思路,我们来实现一下myBind
  Function.prototype.myBind = function (target = window, ...args) {
            let self = this; //指向fn
            let resFn = function (...innerArgs) {//返回一个函数
                // 判断是否是构造函数,即new resFn() 执行的;
                //如果是构造函数,直接指向new的对象,即resFn;
                //如果直接调用,直接使用apply改变this指向
                let isConstuctor = this instanceof resFn; 
                let context = isConstuctor ? this : target; 
                return self.apply(context, args.concat(innerArgs))
            }
            // 如果绑定的是构造函数,那么需要继承构造函数原型属性和方法:保证原函数的原型对象上的属性不丢失
            // 实现继承的方式: 使用Object.create
            resFn.prototype = Object.create(this.prototype);
            return resFn;
        }

执行结果如下

        let myBind1 = fn.myBind(obj, 'myBind1', 1, 2)//一次性传参
        let myBind2 = fn.bind(obj, 'myBind2', 1)
         myBind1(); //使用了myBind1--名字是obj---结果是3
         myBind2(2); //使用了myBind2--名字是obj---结果是3