手写call、apply、bind方法

456 阅读2分钟

相同点:都可以改变函数的上下文this为指定的对象

不同点:

参数接收形式:
  • call接收多个参数,第一参数为this指向,后面为参数列表 Func.call(context,args1,args2,args3...)
  • apply接收两个参数,第一个参数为this指向,后面为一个参数组成的数组 Func.apply(context,[args1,args2,args3...])
  • bind接收参数形式与call类似,接收多个参数 Func.bind(context,args1,args2,args3...)
执行
  • call、apply立即执行,且只改变一次上下文
  • bind不会立即执行,会返回一个永久改变上下文this的函数
传参
  • call、apply一次性传入全部参数
  • bind可以分多次传入参数

手写call

实现思路:在传入的上下文对象上新增方法,即可改变函数的上下文指向为传入的对象
实现步骤:
  1. 获取参数,并且解构为context,和参数列表。arguments->context,args1,args2..
  2. 在传入的context对象中新增函数Func为方法
  3. 执行一次当前的方法,并且传入参数列表
  4. 在context中删除这个方法
  5. 返回值为执行结果
代码实现
        Function.prototype.myCall = function(){
            //初始化,获取传入的this对象和后续的所有参数
            const [context,...args] = [...arguments];
            //在传入的对象上下文中设置属性为将执行的函数this
            context.fn = this;
            //执行一次当前函数,并且传入参数
            let res = context.fn(args);
            //删除属性
            delete context.fn;
            //返回执行结果
            return res;
        }

手写apply

实现步骤
  1. 传入两个参数,context和args
  2. 同样需要在context中添加当前函数this为一个方法
  3. 执行一次这个方法,需要判断是否有args参数
  4. 删除context中的属性
  5. 返回结果为执行结果
代码实现
        Function.prototype.myApply = function(context,args){
            //同样需要在传入的对象上添加方法fn,为当前这个函数
            context.fn = this;
            //判断是否有第二个参数,没有就直接执行,否则需要拼接参数再执行
            let res = !args?context.fn():context.fn(...args);
            delete context.fn;
            return res;
        }

手写bind

实现步骤
  1. 传入的参数和call类似,因此同样需要解构出context和args
  2. 需要返回一个函数,并且接收返回函数传递的值,拼接到bind的传值后面,然后调用apply方法改变this
        Function.prototype.myBind = function () {
           
            const [context, ...args] = [...arguments]
            //返回一个函数
            return function Fn() {
                //接收返回函数中传入的其余参数
                const [...bindArgs] = [...arguments]
                //return 中拼接参数,并且改变上下文
                return self.apply(context, [...args, ...bindArgs])
            }

        }