美文网首页
this、apply、call、bind

this、apply、call、bind

作者: 彼得朱 | 来源:发表于2019-07-05 09:52 被阅读0次

    原文出处:https://juejin.im/post/59bfe84351882531b730bac2

    [TOC]

    1、this指向

    在ES5中,其实this的指向,始终坚持一个原理:this永远指向最后调用它的那个对象,来,读三遍:this永远指向最后调用它的那个对象this永远指向最后调用它的那个对象this永远指向最后调用它的那个对象。记住这句话,下面我们看一个例子:

    //例1
    var name = "windowsName";
    
    function a() {
        var name = "Cherry";
    
        console.log(this.name); // windowsName
    
        console.log("inner:" + this); // inner:[object Window]
    }
    a();
    console.log("outer:" + this) // outer:[object Window]
    

    这个相信大家都知道为什么log的是windowsName,因为根据刚刚的那句话"this 永远指向最后调用它的那个对象",上例中最后调用a的地方a();,前面没有调用的对象那么就是全局对象window,这就相当于window.a();注意,这里我们没有使用严格模式,如果使用严格模式的话,全局对象就是undefined,那么就会报错Uncaught TypeError: Cannot read property 'name' of undefined。

    //例2
    var name = "windowsName";
    var a = {
        name: "Cherry",
        fn: function () {
            console.log(this.name); // Cherry
        }
    }
    a.fn();
    

    在这个例子中,函数fn是对象a调用的,所以打印的值就是a中的name的值。

    做一个小小改动:

    //例3
    var name = "windowsName";
    var a = {
        name: "Cherry",
        fn: function () {
            console.log(this.name); // Cherry
        }
    }
    window.a.fn();
    

    由于这里最后调用fn的对象还是a,所以打印出来仍是Cherry。

    //例4
    var name = "windowsName";
    var a = {
        // name: "Cherry",
        fn: function () {
            console.log(this.name); // undefined
        }
    }
    window.a.fn();
    

    这里打印undefined是由于最后调用fn的对象是a,但是a又没有name属性,所以打印出来是undefined

    这个例子还说明了:this永远指向最后调用它的那个对象,因为最后调用fn的对象是a,所以就算a中没有name属性,也不会继续向上一个对象寻找this.name,而是直接输出undefined。

    //例5
    var name = "windowsName";
    var a = {
        name: "Cherry",
        fn: function () {
            console.log(this.name); // windowsName
        }
    }
    
    var f = a.fn;
    f();
    

    这里你可能会有疑问,为什么不是 Cherry,这是因为虽然将 a 对象的 fn 方法赋值给变量 f 了,但是没有调用,再接着跟我念这一句话:“this 永远指向最后调用它的那个对象”,由于刚刚的 f 并没有调用,所以 fn() 最后仍然是被 window 调用的。所以 this 指向的也就是 window。

    由以上五个例子我们可以看出,this 的指向并不是在创建的时候就可以确定的,在 es5 中,永远是this 永远指向最后调用它的那个对象

    //例6
    var name = "windowsName";
    
    function fn() {
        var name = 'Cherry';
        innerFunction();
    
        function innerFunction() {
            console.log(this.name); // windowsName
        }
    }
    
    fn()
    

    这里是由于:fn()前面没有对象,那就是windows对象,所以打印出来的是windowsName。

    2、怎么改变this指向

    改变 this 的指向总结有以下几种方法:

    • 使用 ES6 的箭头函数
    • 在函数内部使用 _this = this
    • 使用 applycallbind
    • new 实例化一个对象
    //问题提出
    var name = "windowsName";
    
    var a = {
        name: "Cherry",
    
        func1: function () {
            console.log(this.name)
        },
    
        func2: function () {
            setTimeout(function () {
                this.func1()
            }, 100);
        }
    
    };
    
    a.func2() // this.func1 is not a function
    

    上例中,由于this.func1()外面的是setTimeout,也就相当于是window调用的,但是window对象并没有func1(),所以报错。要解决这个问题可以使用以下几种方法。

    (1)使用ES6的箭头函数

    箭头函数:众所周知,ES6的箭头函数可以避免ES5中使用this的坑的。箭头函数的this始终指向函数定义时的this,而非执行时。箭头函数需要记着这句话:“箭头函数中没有 this 绑定,必须通过查找作用域链来决定其值,如果箭头函数被非箭头函数包含,则 this 绑定的是最近一层非箭头函数的 this,否则,this 为 undefined”。

    var name = "windowsName";
    
    var a = {
        name: "Cherry",
    
        func1: function () {
            console.log(this.name)
        },
    
        func2: function () {
            setTimeout(() => {
                this.func1()
            }, 100);
        }
    
    };
    
    a.func2() // Cherry
    

    (2)在函数内部使用_this = this

    除了使用ES6的箭头函数,在函数内部使用 _this = this; 应该是最简单不会出错的方式了。先将调用这个函数的对象保存在 _this中,然后在函数中都使用这个 _this ,这样 _this就不会改变了。

    var name = "windowsName";
    
    var a = {
    
        name: "Cherry",
    
        func1: function () {
            console.log(this.name)
        },
    
        func2: function () {
            var _this = this;
            setTimeout(function () {
                _this.func1()
            }, 100);
        }
    
    };
    
    a.func2() // Cherry
    

    在这个例子中,在func2中,首先设置 var _this = this; ,这里面的this的对象是调用a.func2()中的对象 a ,所以 _this 现在指向的对象也是 a了,所以 _this.func1() 调用的是 a的方法func1。

    (3)使用apply、call、bind

    (a)使用apply

    var a = {
        name: "Cherry",
    
        func1: function () {
            console.log(this.name)
        },
    
        func2: function () {
            setTimeout(function () {
                this.func1()
            }.apply(a), 100);
        }
    
    };
    
    a.func2() // Cherry
    

    (b)使用call

    var a = {
        name: "Cherry",
    
        func1: function () {
            console.log(this.name)
        },
    
        func2: function () {
            setTimeout(function () {
                this.func1()
            }.call(a), 100);
        }
    
    };
    
    a.func2() // Cherry
    

    (c)使用bind

    var a = {
        name: "Cherry",
    
        func1: function () {
            console.log(this.name)
        },
    
        func2: function () {
            setTimeout(function () {
                this.func1()
            }.bind(a)(), 100);
        }
    
    };
    
    a.func2() // Cherry
    

    (d)apply、call、bind 区别

    apply 和 call 的区别:

    apply 和 call 的区别是 call 方法接受的是若干个参数列表,而 apply 接收的是一个包含多个参数的数组。注意:apply是数组apply是数组apply是数组

    // apply
    var a = {
        name: "Cherry",
        fn: function (a, b) {
            console.log(a + b)
        }
    }
    
    var b = a.fn;
    b.apply(a, [1, 2]) // 3
    
    // call
    var a = {
        name: "Cherry",
        fn: function (a, b) {
            console.log(a + b)
        }
    }
    
    var b = a.fn;
    b.call(a, 1, 2) // 3
    

    bind 和 apply、call 区别:

    我们将刚才的例子使用 bind 试一下:

    // bind 没加()的情况
    var a = {
        name: "Cherry",
        fn: function (a, b) {
            console.log(a + b)
        }
    }
    
    var b = a.fn;
    b.bind(a, 1, 2);    //发现并没有输出
    

    为啥呢:MDN上面的文档说明:

    bind()方法创建一个新的函数, 当被调用时,将其this关键字设置为提供的值,在调用新函数时,在任何提供之前提供一个给定的参数序列。

    也就是说,bind是创建一个新的函数,我们需要手动调用,换句话就是需要在后面加上()

    var a = {
        name: "Cherry",
        fn: function (a, b) {
            console.log(a + b)
        }
    }
    
    var b = a.fn;
    b.bind(a, 1, 2)() // 3
    

    (4)new 实例化一个对象

    // 构造函数:
    function myFunction(arg1, arg2) {
        this.firstName = arg1;
        this.lastName  = arg2;
    }
    
    // This    creates a new object
    var a = new myFunction("Li","Cherry");
    a.lastName;                             // 返回 "Cherry"
    

    相关文章

      网友评论

          本文标题:this、apply、call、bind

          本文链接:https://www.haomeiwen.com/subject/kiwjhctx.html