手写bind/call/apply

·  阅读 215

导读

  1. 需要先了解 bind/call/apply 函数使用与作用 (作用就是改变 this 的指向
  2. 需要先了解 this (this 永远指向最后调用它的那个对象
  3. 需要了解原型与原型链
  4. 需要了解 instanceof
  5. 带个问题看,bind 只有在第一次的时候绑定的 this 有效,是吗?

简易 bind 实现 (未考虑new的情况)

Function.prototype.myBind = function (that) {
    // 获取指向的对象
    console.log('that', that); // that {name: 'kevin'}
    // 取出所有除绑定目标对象外的全部参数
    let params1 = Array.prototype.slice.call(arguments, 1);
    console.log('params1', params1); // [1, 2 ,3]
    console.log('this', this); // fn1函数
    const self = this;
    // 返回一个函数
    function bound () {
        // 获取bind后调用的传入参数
        const params2 = Array.prototype.slice.call(arguments);
        console.log('params2', params2); // [100, 200]
        return self.apply(that, params1.concat(params2));
    };
    return bound;
};

function fn1 () {
    console.log('this', this); // this {name: 'kevin'}
    console.log(arguments); // [1, 2 ,3]
    return 'this is fn1';
};

const fn2 = fn1.bind({ name: 'kevin'}, 1, 2, 3);
// const fn2 = fn1.myBind({ name: 'kevin'}, 1, 2, 3);
const res = fn2(100, 200);
console.log(res); // this is fn1
复制代码

bind 实现

Function.prototype.myBind = function (that) {
    let params1 = Array.prototype.slice.call(arguments, 1);
    const self = this;
    // 构建一个干净的函数,用于保存原函数的原型
    const nop = function () { };
    function bound () {
        let params2 = Array.prototype.slice.call(arguments);
        // console.log(self) // a 这个函数
        // console.log(this) // window
        // console.log(this) // new c() // bound {}
        // console.log(that) // b 这个对象
        return self.apply(
            // this instanceof nop, 判断是否使用 new 来调用 bound
            // 如果是 new 来调用的话,this的指向就是其实例,
            // 如果不是 new 调用的话,就改变 this 指向到指定的对象 that
            this instanceof nop ? this : that,
            params1.concat(params2)
        );
    };
    // 箭头函数没有 prototype,箭头函数this永远指向它所在的作用域
    if (this.prototype) {
        nop.prototype = this.prototype;
    };
    // 修改绑定函数的原型指向
    bound.prototype = new nop();
    return bound;
};

const a = function () {
    // // c(100, 200);
    // console.log(arguments); // [1, 2, 3, 100, 200]
    // console.log(this.name); // kevin
    // // new c(300);
    console.log(arguments); // [1, 2, 3, 300]
    console.log(this.name); // leslie
};
a.prototype.name = 'leslie';
const b = { name: 'kevin' };

// const c = a.bind(b, 1, 2, 3);
const c = a.myBind(b, 1, 2, 3);

// c(100, 200);
new c(300);
复制代码

call/apply 本质是一样的,都是改变 this 指向后执行函数,区别只是入参不一样而已

Function.prototype.myCall = function (that) {
    // 声明一个 Symbol 属性,防止 fn 被占用
    const fn = Symbol('fn')
    const params = [...arguments].slice(1);
    that = that || window;
    that[fn] = this;
    const result = that[fn](...params);
    delete that[fn];
    return result;
}

Function.prototype.myApply = function (that) {
    const fn = Symbol('fn')
    const params = arguments[1];
    that[fn] = this;
    const result = that[fn](...params);
    delete that[fn];
    return result;
};

const a = function () {
    console.log(this.name, arguments);
};

a.prototype.name = 'a';

const foo = {
    name: 'foo'
};

a.myCall(foo, 1, 2, 3); // foo [1, 2, 3]
a.myApply(foo, [1, 2, 3]); // foo [1, 2, 3]
复制代码

小结

希望看完本篇文章能对你有如下帮助:

  • bind 只有在第一次的时候绑定的 this 有效? 答案:是的。
  • 了解 bind/call/apply 原理。
  • 更多的是为了在实现过程中,熟悉与回顾其他的知识点。

文中如有错误,欢迎在评论区指正,如果这篇文章帮助到了你,欢迎点赞和关注。

往期内容推荐

  1. 深拷贝
  2. 轻松理解JS原型原型链
  3. new 操作符
分类:
前端
标签:
收藏成功!
已添加到「」, 点击更改