手写call、apply、bind

概述

call、apply、bind均可以改变函数的this指向,其中call、apply会改变函数的this指向并执行函数,而bind只会改变函数的this指向并不会执行函数。这三个方法的第一个参数同样是指this。如果函数的调用需要传递参数,call的第二个参数,第三个参数……传递的是函数调用的参数;apply的第二个参数是一个数组,包含要传递的全部参数。

fn.call(target, arg1, arg2, ...)

fn.apply(target, [arg1, arg2, ...])

fn.bind(target)

call

   Function.prototype.myCall=function(context){
        if(typeof this!=='function'){
            throw new TypeError('Error');
        }
        context = context || window;
        context.fn = this;
        const args = [...arguments].slice(1);
        const result = context.fn(...args);
        delete context.fn;
        return result;
    }
复制代码

详解:

  • context = context || window;

    在ECMAScript 3和非严格模式中,传入call的第一个参数,如果传入的值为null或者undefined都会被全局对象代替,而其他的原始值则会被相应的包装对象(wrapper object)所替代。所有的引用类型(array/object/function),都具有对象特性,即可自由扩展属性(null除外) ,所以除了引用类型和null/undefined外,其他数据类型要转化为相应的包装类型,使其可以扩展属性。

    此处可做优化

        const type = typeof context;
        if(context === undefined || context === null){
            type = 'window';
        }
        switch(type){
            case 'window':
                context = window;
                break;
            case 'number':
                context = Number(context);
                break;
            case 'string':
                context = String(context);
                break;
            case 'boolean':
                context = Boolean(context);
                break;
            default:
                context = context;
        }
    复制代码
  • context.fn = this;

    此处的this是要改变this指向的函数本身,将这个函数作为context的一个属性,可以改变这个函数的this指向。

apply

 Function.prototype.myApply = function(context){
    if(typeof this !== 'function'){
        throw new TypeError('Error');
    }
    context = context || window;
    context.fn = this;
    let result;
    if(arguments[1]){
        result = context.fn(...arguments[1]);
    }else{
        result = context.fn();
    }
    delete context.fn;
    return result;
}
复制代码

详解:同上。

bind

Function.prototype.myBind = function(context){
    if(typeof this !== 'function'){
        throw new TypeError('Error');
    }
    const _this = this;
    const argus = [...arguments].slice(1);
    return function F(){
        //因为返回了一个函数,可以new F(),所以需要判断
        if(this instanceof F){
            return new _this(...argus,...arguments);
        }
        return _this.apply(context,argus.concat(...arguments));
    }
}
复制代码

详解:

  • 其它知识:用new创建构造函数的实例,会经历一下四个步骤:

    1).创建一个新对象;

    2).将这个构造函数的作用域赋给新对象(因此this就指向了这个新对象)

    3).执行构造函数中的代码(为这个新对象添加属性);

    4).返回新对象。

  • bind返回的是一个函数(闭包)

  • this instanceof Freturn new _this(...argus,...arguments);

    判断this是不是通过new F(),如果是的话相当于执行new fnName()(即new _this()),此时会创建一个该函数的新对象,此时的this指向了这个新对象,并且执行构造函数中的代码(带上参数)。

  • return _this.apply(context,argus.concat(...arguments));

    此处执行myBind返回的闭包,原理同apply。

转载于:https://juejin.im/post/5ce0f041e51d4510712135cb

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值