面试常考点:手写一个call和apply

面试的时候call和apply是经常被问到的点,它们可以用来改变函数的this指向,那么是怎么实现的呢?

首先我们定义一个需要用到的实例代码

const name = 45456;
const obj = {
    name: 111,
    fn: function (param1, param2) {
        console.log(this.name, param1, param2);
    },
};

我们调用自带的call方法

obj.fn.call(null, 2, null);//obj.fn.call(window, 2, null);

在浏览器输出的this都会是window,也就是this.name45456

手写call方法: 

 我们自己写一个模拟call方法,直接上代码(需要注意的点我都写在注释里面了)

Function.prototype.mycall = function (fnThis) {
        //方法要添加的Function的原型链上面
        const newThis = Object(fnThis || window); //要改变的this,一定要是个Object类型,否则类似字符串类型的会报错
        const argParams = [];
        newThis._callFn = this; //这里的this指的是函数本身
        for (let i = 1; i < arguments.length; i++) {
          // arguments本身是个类数组,所以定义一个数组去存放
          argParams.push(arguments[i]);
        }
        newThis._callFn(...argParams); // 也可以写成 eval("newThis.fn(" + argParams + ")"); 不过传入null会默认转成undefined
        delete newThis._callFn; //把刚刚新增的函数删掉
      };

调用一下,this指向没有问题

console.log("自己封装的call:");
obj.fn.mycall(null, null, 2);

 把this指向自己,name是111,也没错

console.log("自己封装的call:");
obj.fn.mycall(obj, null, 2);

 

 

 那么call和apply唯一的不同点就是:call可以多个参数传入,而apply传入的第二个参数必须是数组

先调用自带的apply函数

console.log("自带的apply:");
obj.fn.apply(null, [null, 11]);

手写apply方法

那么贴上自己封装的apply函数,其本质上是跟call没啥差别,只是多了判断

Function.prototype.myapply = function (fnThis) {
        //方法要添加的Function的原型链上面
        const newThis = Object(fnThis || window); //要改变的this,一定要是个Object类型
        const params = arguments[1];
        const arrParams = [];
        newThis._applyFn = this; //这里的this指的是函数本身
        // 判断一下第二个参数是否是数组
        if (Array.isArray(params)) {
          for (let i = 0; i < params.length; i++) {
            arrParams.push(params[i]);
          }
        }
        newThis._applyFn(...arrParams);
        delete newThis._applyFn; //把刚刚新增的函数删掉
      };

 那么细心的朋友可能会发现,自带的apply如果参数传的不是一个数组可能会抛出错误,例如这样:

obj.fn.apply(null, 11);

 那么为了解决这个问题,我们在原来的基础上加多一个判断

Function.prototype.myapply = function (fnThis) {
        //方法要添加的Function的原型链上面
        const newThis = Object(fnThis || window); //要改变的this,一定要是个Object类型
        const params = arguments[1];
        const arrParams = [];
        newThis._applyFn = this; //这里的this指的是函数本身
        // 判断一下第二个参数是否是引用类型
        if (typeof params !== "object") {
          throw new TypeError("CreateListFromArrayLike called on non-object");
        }
        // 判断一下第二个参数是否是数组
        if (Array.isArray(params)) {
          for (let i = 0; i < params.length; i++) {
            arrParams.push(params[i]);
          }
        }
        newThis._applyFn(...arrParams);
        delete newThis._applyFn; //把刚刚新增的函数删掉
      };
console.log("自己封装的apply:");
obj.fn.myapply("hello", 11);

 

console.log("自己封装的apply:");
obj.fn.myapply(window, [11, { name: 11 }]);

 this指向window,输出也是对的

 

 这下就完美了!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值