经典面试题系列(4):手写原生call方法(内含call方法超详细实现原理)

812 阅读3分钟

1.由一道面试题引发我对于call方法原理的思考

看了这道题如果对于call方法原理不懂的小伙伴肯定要懵逼了,我们都知道call用来改变函数执行时候,方法中的THIS指向。但是自己手写一个到底是怎么实现的呢??在百度搜索相关的内容一般都是介绍call方法的用法而没有涉及原理,在自己解决这个问题之后,决定记录一下分享给大家

  • 下面看题吧
~function(){
    function myCall(){
        //=>实现你的代码
    };
    Function.prototype.myCall=myCall;
}();
let obj = {name:'Alibaba'};
function func(x,y){
    this.total=x+y;
    return this;
}
let res = func.myCall(obj,100,200);
//res => {name:'Alibaba',total:300}

2.我们代码加注释的详细讲解看一下这道题的深度

注意:细节点还是慢慢的,一不小心就懵逼了。笔者对每一步都进行了详细的阐述,掘金上Markdown我不会改变背景色,注释字体看着比较浅,读者可以粘贴到自己的编辑器里查看

/*
 * 以这道题展开思考,我们想改变func函数中的THIS指向obj 
 * func:当前要执行的方法
 *   @params
 *      context:要改变的THIS指向(需要是一个对象,如果不是对象,可以把它变为对象)
 *      arg: 用剩余运算符接受的的参数都是未来执行func函数的时候,传递给函数的参数
 *   @return 
 * 		--
 * 实现原理:让func和context之间建立关联(func是context的一个属性的属性值)
 */
 function myCall(context, ...arg) {
    //=>我的实现代码
    //首先对于context为空的情况进行处理
    context == null ? context = window : null;
    //保证context传入的是对像,如果不是将其转化为对象
    if (typeof context !== 'object' || typeof context !== 'function'){
        //详细说说这一步的操作,第一次看到可能不太好理解,
        //以一个数字为例加入形参context = 1,其实这一句代码等同于
        //context = new Number(context);,也就是context会根据constructor去找到自己类型所属的包装类
        context = new context.constructor(context);
    }
    //创建一个唯一值,避免与context本身的属性产生冲突
    let uniqueKey = `?${new Date().getTime()}`;
    //将原本要执行的函数以我们创建的为一致为属性赋给context对象,这一带你比较难理解的事this
    //this既是我们的调用者func函数,看我们的调用func.myCall(obj,100,200);
    context[uniqueKey] = this;
    //函数执行结果,我们执行func方法,但调用这改为了我们的context,也就是obj
    let result = context[uniqueKey](...arg);
    //删除掉我们新增的属性,保证使用前后对象的准确性
    delete context[uniqueKey];
    return result;
};
//将我们自己封装的方法添加到函数的原型上,方便每一个方法调用
Function.prototype.myCall = myCall;

持续更新手写原生js内置方法系列,帮助大家更加的了解js原生一些方法的原理。如果对你有所帮助,别忘点个赞支持一下哦