欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

手写call、apply、bind

程序员文章站 2022-03-08 09:20:40
...

概述

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