this指向及call.apply.bind问题

63 阅读3分钟

this指向及call.apply.bind问题

开发和面试中都免不了不到函数指向的问题,并伴随着改变this指向的call.apply.bind的方法的使用。

一、this的指向

  • 记住这句话:this永远指向最后调用他的那个对象
  • 任何函数本质上都是通过某个对象来调用的,如果没有直接指定就是window
  • 所有函数内部都有一个变量this
//代码示例 1:
    var name = "window_Name";
        function show() {
            var name = "watson";

            console.log(this.name);   

            console.log("inner:" + this);    
        }
    a();
    console.log("outer:" + this)       
//输出为: window_Name   inner: Window  outer: Window

:代码示例1中,最后调用的a(),前面没有对象,相当于在全局内调用的,可以理解为在当前window环境下面调用的a() --->window.a,所以根据前面说的 this永远指向最后调用他的那个对象 ,注意在严格模式下,全局对象是undefined,那么会报错:Uncaught TypeError: Cannot read property 'name' of undefined

//代码示例 2:
    var name = "window_Name";
    var a = {
        name: "watson",
        fn : function () {
            console.log(this.name);      
        }
    }
    a.fn();
    window.a.fn();
//输出为: watson  watson

代码示例2中 a.fn()其实就是在window下面调用的,所以在a.fn()和window.a.fn()中的fn函数的指向都是a (this永远指向最后调用他的那个对象 ) 所以输出都是一样的 watson。

//代码示例 3:
    var name = "window_Name";
    var a = {
        // name: "watson",
        fn : function () {
            console.log(this.name);      
        }
    }
    a.fn();
//输出 undefined

:代码示例3中,fn函数的直接调用对象是a,也就是说他所指向的内部的this指向,指向的是a对象,但是在a对象中并没有对name进行定义,所以输出的是undefined

//代码示例 4:
    var name = "window_Name";
    var a = {
        name: "watson",
        fn : function () {
            console.log(this.name);  
        }
    }

    var f = a.fn;
    f();
//输出 window_Name

在代码示例 4 中,我们将a对象中的方法fn赋值到了变量f中,然后在外层的window环境下面调用,其实就是window.f(),所以fn函数里面的fn其实指向的是window,this永远指向最后调用他的那个对象 ,那么输出的便是 window_Name,而不是watson。

//代码示例 5:
    var name = "window_Name";

    function fn() {
        var name = 'watson';
        a();
        function a() {
            console.log(this.name);  
        }
    }

    fn()
//输出 window_Name

代码示例5 中fn调用是,执行了当前的a函数,在a函数而言,还是在当前的window对象下面执行的,所以this.name还是指向的是window_Name

二、改变this指向

  • 使用 ES6 的箭头函数
  • 在函数内部使用 _this = this
  • 使用 apply、call、bind 。参考 MDN 中定义
  • new 实例化一个对象

:箭头函数

    var name = "window_Name";
    var a = {
        name : "watson",

        func1: function () {
            console.log(this.name)     
        },

        func2: function () {
            setTimeout(  function () {
                this.func1()
            },100);
        }
        func3: function () {
            setTimeout( () =>{
                this.func1()
            },100);
        }
    };
    a.func2()    
//输出: 1、this.func1 is not a function    2、watson
  • 函数的情况下,是会报错的,因为最后调用 setTimeout 的对象是 window,但是在 window 中并没有 func1 函数,
  • 箭头函数的 this 始终指向函数定义时的 this,而非执行时。箭头函数需要记着这句话:“箭头函数中没有 this 绑定,必须通过查找作用域链来决定其值,如果箭头函数被非箭头函数包含,则 this 绑定的是最近一层非箭头函数的 this,否则,this 为 undefined”。

使用_this = this

    var name = "window_Name";

    var a = {

        name : "watson",

        func1: function () {
            console.log(this.name)     
        },

        func2: function () {
            var _this = this;
            setTimeout( function() {
                _this.func1()
            },100);
        }

    };

    a.func2() 
//输出 : watson
  • 首先设置 var _this = this;,
  • 这里的 this 是调用 func2 的对象 a,为了防止在 func2 中的 setTimeout 被 window 调用而导致的在 setTimeout 中的 this 为 window。我们将 this(指向变量 a) 赋值给一个变量 _this,这样,在 func2 中我们使用 _this 就是指向对象 a 了。

③ 使用 apply、call、bind

  • apply
    var a = {
        name : "watson",

        func1: function () {
            console.log(this.name)
        },

        func2: function () {
            setTimeout(  function () {
                this.func1()
            }.apply(a),100);
        }

    };

    a.func2()            // watson

  • call
    var a = {
        name : "watson",

        func1: function () {
            console.log(this.name)
        },

        func2: function () {
            setTimeout(  function () {
                this.func1()
            }.call(a),100);
        }

    };

    a.func2()            // watson
  • bind
    var a = {
        name : "watson",

        func1: function () {
            console.log(this.name)
        },

        func2: function () {
            setTimeout(  function () {
                this.func1()
            }.bind(a)(),100);
        }

    };

    a.func2()            // watson

call.apply.bind的区别?

  • call 方法接受的是若干个参数列表,apply 接收的是一个包含多个参数的数组
  • bind 是创建一个新的函数,我们必须要手动去调用
//代码示例:
    //call的使用
    var a ={
        name : "watson",
        fn : function (a,b) {
            console.log( a + b)
        }
    }
    var b = a.fn;
    b.call(a,1,2)       // 3
    
    //apply的使用
    var a ={
        name : "watson",
        fn : function (a,b) {
            console.log( a + b)
        }
    }
    var b = a.fn;
    b.apply(a,[1,2])     // 3
    
    //bind的使用
    var a ={
        name : "Cherry",
        fn : function (a,b) {
            console.log( a + b)
        }
    }
    var b = a.fn;
    b.bind(a,1,2)()           // 3