call函数的模拟实现

29 阅读2分钟

call函数的正常调用

function.call(thisArg)  //修改this绑定指向thisArg
let foo =  {
    value :1
}
function bar(){
    console.log(this.value)
}
bar.call(foo); // 输出结果为1

call方法原理

1.将函数临时设置为对象的属性

2.使用该对象调用函数

3.删除临时添加的属性

4.返回函数执行的结果

注意事项:

thisArg为null或undefined时,this指向全局对象

需要处理参数传递

函数可能会有返回值

ES5版本call函数方法模拟

//1.通过Function.prototype添加myCall方法,使所有函数可用
Function.prototype.myCall = function (context){
    //2.处理上下文参数,如果context为null或undefined,则指向全局对象
    var context = context || window;
    //3.将当前函数(this)设置为context的临时属性fn
    // 这里的this可用理解为调用myCall的函数对象
    context.fn = this;
    //4.获取传入的参数,除了第一个context参数
    const args = [];
    //arguments 是函数内部的一个类数组对象,包含调用函数时传入的所有参数
    for(let i = 1;i<arguments.length;i++){
        args.push('arguments[' + i + ']');
    }
    //5.执行函数,使用eval函数拼接参数
    const result = eval('context.fn('+ args + ')');
    //6.删除临时添加的属性
    delete context.fn;
    //7.返回指向的结果
    return result;
}
示例解析ES5版本call函数方法模拟
function greet(name){
    console.log(`hello,${name}! I am ${this.title}`);
}
const person = {
    title: 'LiHua'
}
greet.myCall(person,'John');  //hello,John! I am LiHua

当调用greet.myCall(person,'John');时

myCall内部的this指向 当调用greet函数

context指向 person 对象

context.fn = this; ===> person.fn = greet; ===> person.fn('John');

此时fn中的this指向person

ES6版本call函数模拟

//1..通过Function.prototype添加myCallES6方法,使所有函数可用
Function.prototype.myCallES6 = function (context,...args){
    //2.处理上下文参数,如果 context 为 null 或 undefined,默认指向全局对象
    context = context || window;
    // 3.根据环境选择属性名策略
    const useSymbol = typeof  Symbol !== 'undefined';
    /*
    *4.检测当前环境是否支持 Symbol
    * 现代浏览器/Node.js: Symbol 已定义 → useSymbol = true,支持 Symbol: 创建唯一Symbol → Symbol()
    * 老旧环境(如IE): Symbol 未定义 → useSymbol = false,不支持 Symbol: 生成随机字符串 → `__fn_${时间戳}_${随机数}`
    * */
    const propName = useSymbol ? Symbol():`_fn_${Date.now()}_${Math.random()}`;
    /*
    * 5.将当前函数绑定到上下文,同ES5版本一样即设置当前函数(this)为context的临时属性propName
    * 这里的this可用理解为调用myCallES6的函数对象
    * */
    context[propName] = this;
    //6.直接执行函数
    const result = context[propName](...args);
    //7.清理临时属性
    delete context[propName];
    //8.返回结果
    return result;
}
相同示例解析ES6版本call函数模拟
greet.myCallES6(person,'John'); //hello,John! I am LH

person ===> context

args ===> ['John']

propName = Symbol() 或 随机字符串

this ===> greet函数

person[propName] ===> greet函数

执行

 person[propName]('John')

此时propName中的this指向person

result = 'hello,John! I am LH'