JavaScript手写call,apply,bind

JavaScript手写call,apply,bind

call,apply,bind,都能在JavaScript中改变this指向,那么如何用原生的JS来实现呢?

call

实现思路

  1. 参考call的语法规则,需要设置一个参数thisArg,也就是this的指向;

  2. thisArg封装为一个Object;

  3. 通过为thisArg创建一个临时方法,这样thisArg就是调用该临时方法的对象了,会将该临时方法的this隐式指向到thisArg上;

  4. 执行thisArg的临时方法,并传递参数;

  5. 删除临时方法,返回方法的执行结果。

    /*原生实现call*/
    Function.prototype.mycall = function(thisArg, ...arr){
                //先判断合不合法
                if(thisArg === undefined || thisArg === null){
                    //指向全局window
                    thisArg = window;
                }else{
                    //合法创建一个对象
                    thisArg = Object(thisArg);
                }
                //为thisArg创建一个临时方法
                let specialMethod = Symbol('anything');
                //这个this是保存的调用函数
                thisArg[specialMethod] = this;
                let result = thisArg[specialMethod](...arr);
                //删除临时方法
                delete thisArg[specialMethod];
                //返回结果
                return result;
    
            }
            test1 = {
                str:'test1'
            }
            function test2() {
                str = 'test2'
                console.log(this.str);
            }
            test2()//test2
            test2.mycall(test1)//test1
    
apply
  1. apply是第2个参数,这个参数是一个类数组对象:传给func参数都写在数组中
Function.prototype.myapply = function(thisArg){
            //先判断合不合法
            if(thisArg === undefined || thisArg === null){
                //指向全局window
                thisArg = window;
            }else{
                //合法创建一个对象
                thisArg = Object(thisArg);
            }
            //为thisArg创建一个临时方法
            let specialMethod = Symbol('anything');
            //这个this是保存的调用函数
            thisArg[specialMethod] = this;
            //apply第二个参数为类数组对象
            function isArrayLike(o){
                if(o && typeof o === "object" && isFinite(o.length) && o.length >= 0 && // o.length为非负值
                o.length === Math.floor(o.length) && // o.length是整数
                o.length < 4294967296){
                return ture;
                }else{
                    return false;
                }
            }
            let args = arguments[1]; // 获取参数数组
            let result;
            if(args){
                if (!Array.isArray(args) && !isArrayLike(args)) {
                    throw new TypeError(
                        "第二个参数既不为数组,也不为类数组对象。抛出错误"
                    );
                } else {
                    args = Array.from(args); // 转为数组
                    result = thisArg[specialMethod](...args); // 执行函数并展开数组,传递函数参数
                }
            }else{
                thisArg[specialMethod]();
            }
            //删除临时方法
            delete thisArg[specialMethod];
            //返回结果
            return result;
        }
        test2()//test2
        test2.myapply(test1);//test1
bind
实现思路

(1)拷贝调用函数:

  • 调用函数,也即调用myBind的函数,用一个变量临时储存它;
  • 使用Object.create复制调用函数的prototype给funcForBind

(2)返回拷贝的函数funcForBind

(3)调用拷贝的函数funcForBind

  • new调用判断:通过instanceof判断函数是否通过new调用,来决定绑定的context;
  • 通过call绑定this、传递参数;
  • 返回调用函数的执行结果。
Function.prototype.mybind = function(objThis, ...params){
            const thisFn = this;//存储调用函数
            let funcForBind = function(...secondParams) {
                //检查this是否是funcForBind的实例?也就是检查funcForBind是否通过new调用
                const isNew = this instanceof funcForBind;

                //new调用就绑定到this上,否则就绑定到传入的objThis上
                const thisArg = isNew ? this : Object(objThis);

                //用call执行调用函数,绑定this的指向,并传递参数。返回执行结果
                return thisFn.call(thisArg, ...params, ...secondParams);
            };

            //复制调用函数的prototype给funcForBind
            funcForBind.prototype = Object.create(thisFn.prototype);
            return funcForBind;//返回拷贝的函数 
        }
        let func = function(p,secondParams){//其实测试用的func其参数可以是任意多个
            console.log(p.name);
            console.log(this.name);
            console.log(secondParams);
        }
        let obj={
            name:"1891"
        }
        func.mybind(obj,{name:"coffe"})("二次传参");
        //>> coffe
        //>> 1891
        //>> 二次传参
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值