bind、call、apply

22 阅读2分钟

一、作用

这三个函数能改变函数执行时的上下文,即改变函数运行时的this指向。 setTimeout方法中参数里面的函数是作为回调函数来执行的,回到主栈执行时在全局执行上下文的环境中执行,这个时候的this指向window(即默认绑定)

二、区别

  1. apply apply接受两个参数,第一个参数是this指向,第二个参数是函数接受的参数,以数组形式传入,改变this指向后原函数会立即执行,且只临时改变this指向一次 第一个参数为null、undefine的时候默认指向window
  2. call call方法第一个参数也是this指向,后面传入的是一个参数列表,改变this指向后原函数会立即执行,且只临时改变this指向一次 第一个参数为null、undefine的时候默认指向window
  3. bind 与call类似,第一个参数是this指向,后面传入一个参数列表(但这个参数列表可以分多次传入) 改变this的指向后不会立即执行,而是返回一个永久改变this指向的函数

三者的区别在于:

  • 三者都可以传参,但apply是数组,call和bind是参数列表;且apply和call是一次性传入参数,而bind可以分多次传入
  • apply、call是立即执行,bind是返回绑定this之后的函数

三、实现

    // call实现代码
    // 定义一个新的对象,若传入的obj存在,则新对象等于obj, 若obj不存在,则等于window;
    // 把this挂在到当前定义的新对象上(this即为调用的函数);
    // 第4行代码处理了函数的传参;
    // 然后执行创建的新对象的fn函数(即为要调用的函数);
    // 最后在执行了以后,把这个挂载的fn删除;
    Function.prototype.MyCall = function (...args) {
        var newObj = arguments[0] || window;
        newObj.fn = this;
        var params = [...arguments].slice(1);
        var result = newObj.fn(...params);
        delete newObj.fn;
        return result;
    }
    function test(...args) {
        console.log(this, args);
    }
    test(123);
    test.MyCall({ name: "西瓜" }, 123);

    // 1.修改this指向
    // 2.动态传递参数
    // 3.兼容new关键字
    function fn(...args) {
        console.log(this, args);
    }
    let obj = {
        name: 'car'
    }
    // bind的两种调用方式
    fn.bind(obj, 111)()
    fn.bind(obj, 1)(12)
    // bind实现代码
    Function.prototype.mybind = function (context) {
        // 判断调用对象是否为函数
        if (typeof this !== "function") {
            throw new TypeError("Error");
        }
        // 获取参数
        const args = [...arguments].slice(1),
            fn = this;
        return function Fn() {
            return fn.apply(this instanceof Fn ? new fn(...arguments) : context, args.concat(...arguments)); 
        }
    }