手写 call、apply、bind 函数

  call

    var name = 'waq';
    var obj = {
        name: 'goodLuck'
    }
    function sayName(){
        console.log(this.name);
    }
    
    //1、改变this指向函数
    Function.prototype.myCall = function (context){
        context = context || window;
        var args = [...arguments].slice(1);  //第一个参数为context
        context.fn = this;   //因为call的调用方式形如:sayName.call(obj),因此此时call方法的this指向为sayName,因此context.fn = this即为context.fn = sayName
        var result = context.fn(...args);
     delete context.fn;
return result; } //2、验证 //没有改变this指向时 sayName(); //waq //改变this指向后 sayName.myCall(obj); //goodLuck
  • 首先 context 为可选参数,如果不传的话默认上下文是 window
  • 因为 call 可以传入多个参数作为调用函数的参数,所以将参数单独抽取出来
  • 接下来给 context 创建一个 fn 属性,并将值设置为需要调用的函数
  • 删除对象上的函数,释放内存空间。返回结果

 

  apply

 apply 和 call 实现类似,不同的是参数处理,apply 传入的是数组。

    Function.prototype.myApply = function (context){
        context = context || window;
        context.fn = this;
        var result;
        if (arguments[1]){
            result = context.fn(...arguments[1]);
        }else{
            result = context.fn();
        }
        delete context.fn;
        return result;
    }

 

  bind

 bind 会创建一个新函数,不会立即执行。bind 后面传入的这个参数列表可以分多次传入,call 和 apply 则必须一次性传入所有参数。

    Function.prototype.myBind = function (context){
        context = context || window;
        //返回一个绑定this的函数,这里我们需要保存this,具体保存的this如同call
        let self = this;
        let args = [...arguments].slice(1);
        //返回一个函数
        return function () {
            let newArgs = [...arguments];
            return self.apply(context, args.concat(newArgs));
        }
    }

 return function是因为 bind 返回的是一个函数,并且这个函数不会执行,需要我们再次调用,那么当我们调用的时候,我们依旧可以对这个函数进行传递参数,即为支持柯里化形式传参,如下:

  bind 可以实现类似这样的代码 f.bind(obj,1)(2)

 所以需要在返回的函数中声明一个空的数组接收调用 bind函数返回的函数时传递的参数,之后对两次的参数使用concat()方法进行连接,调用ES5中的apply方法,于是就有了这样的实现 args.concat(newArgs).

 

posted @ 2021-11-11 20:40  打遍天下吴敌手  阅读(73)  评论(0编辑  收藏  举报