call、apply和bind的极简实现

97 阅读2分钟

我们知道call,apply和bind都是用来改变this指向的内置方法,简单问就是三者的区别,复杂问就是手写实现,下面就来简单介绍下三种方法的实现。

一、call

Function.prototype._call = function (content, ...args) {
    // 获取当前的执行环境,如果不存在返回window,这里相当于test._call(obj, 'bqb', '水中水')中的obj
    content = content || window;
    // 定义全局唯一的Symbol
    let fn = Symbol();
    // 在content中新增this函数,相当于test._call(obj, 'bqb', '水中水')中的test
    content[fn] = this;
    // 通过content[fn](...args)的方式,执行content中的方法fn,相当于,执行obj中的test,主体已经变成了obj了,例子中的this就是obj。
    return content[fn](...args)
}

测试用例:

var obj = {
    name: 'qb'
}

var test = function (nickName1, nickName2) {
    console.log(this.name, nickName1, nickName2);
}

test._call(obj, 'bqb', '水中水')

二、apply

Function.prototype._apply = function (content, args) {
    // 获取当前的执行环境,如果不存在返回window,这里相当于test._call(obj, 'bqb', '水中水')中的obj
    content = content || window;
    // 定义全局唯一的Symbol
    let fn = Symbol();
    // 在content中新增this函数,相当于test._apply(obj, 'bqb', '水中水')中的test
    content[fn] = this;
    // 通过content[fn](...args)的方式,执行content中的方法fn,相当于,执行obj中的test,主体已经变成了obj了,例子中的this就是obj。
    return content[fn](...args)
}

测试用例:

var obj = {
    name: 'qb'
}

var test = function (nickName1, nickName2) {
    console.log(this.name, nickName1, nickName2);
}

test._apply(obj, ['bqb', '水中水'])

三、bind

Function.prototype._bind = function (context, ...args) {
    // 获取当前的执行环境,如果不存在返回window
    context = context || window;
    // 定义全局唯一的Symbol
    let fn = Symbol();
    // 在content中新增this函数,相当于test._apply(obj, 'bqb', '水中水')中的test
    context[fn] = this;
    // 将当前this保存到_this中去,这里的this指的是例子中的test
    var _this = this;

    return function F() {
        if (this instanceof F) {
            // 此时this指向指向F创建的实例;
            this[fn] = _this;
            this[fn](...args);
            delete this[fn];
        } else {
            // 此时的this指向传入的content;
            context[fn](...args);
        }
    };
};

new场景测试案例:

// new场景测试案例
var obj = {
    name: 'qb'
}

var test = function (nickName1, nickName2) {
    this.nickName1 = nickName1;
    this.nickName2 = nickName2;
}
var F = test._bind(obj, 'bqb', '水中水');
var f = new F();

直接调用场景测试案例

var obj = {
    name: 'qb'
}

var test = function (nickName1, nickName2) {
    console.log(this.name, nickName1, nickName2)
}
var F = test._bind(obj, 'bqb', '水中水');
F();

总结

callapply的实现是通过content[fn] = this的方式存储当前环境,然后通过content[fn](...args)的方式调用fn方法,本着谁调用就this就指向谁的原则,this就被指向了contentbind的实现在直接调用场景和call及其apply是一致的,在new场景是通过this[fn] = _this的方式储存当前方法,然后通过this[fn](XXX)的方式调用方法,这是的this就是构造函数创建的实例。