javascript的this指向问题深度解析

参考来源[https://segmentfault.com/a/1190000008400124]

  1. javascript函数中的this指向并不是函数定义的时候确定的,而是在调用的时候确定的。相当于,函数的调用方式决定了this指向。

  2. js中普通函数的调用有三种方试:

    • 直接调用

    • 方法调用

    • new调用

    • 除外,还有些比如通过bind()将函数绑定到对象之后再调用、通过call()、apply()进行调用等。

  3. 直接调用的this指向问题

    • 就是直接通过函数名这种方式调用。这时候的函数内部的this指向全局对象,在浏览器中全局对象是window,在nodejs中全局对象是global。

    • 需要注意的是,直接调用并不是指在全局作用域下调用,在任何作用域下,直接通过函数名()来对函数进行调用的方式,都是直接调用。

    • bind()对直接调用的影响

    • function.prototype.bind()的作用是将当前函数与指定的对象绑定,并返回一个新函数,这个新函数无论以什么样的方式调用,其this始终指向绑定的对象。

    var obj = {};
    function test() {
        console.log(this===obj);
    }
    var testObj = test.bind(obj);
    test(); //false
    testObj(); //true
    
    
    • call()和apply()对this的影响

      1. function.prototype.apply()与function.prototype.call(),他们的第一个参数都是指定函数运行时其中的this指向。

      2. 不过使用aplly和call的时候,要注意,如果目录函数本身就是一个绑定了this对象的函数,那apply和call不会像预期的那样执行(所以慎用bind())。

          const obj = {};
      
          function test() {
              console.log(this === obj);
          }
          
          // 绑定到一个新对象,而不是 obj
          const testObj = test.bind({});
          test.apply(obj);    // true
          
          // 期望 this 是 obj,即输出 true
          // 但是因为 testObj 绑定了不是 obj 的对象,所以会输出 false
          testObj.apply(obj); // false
          
      
  4. 方法调用

    • 方法调用是指通过对象来调用其方法函数,它是对象.方法函数()这样的调用形式。这种情况下,函数中的this指向调用该方法的对象。但是,同样需要注意bind()的影响。
        const obj = {
            // 第一种方式,定义对象的时候定义其方法
            test() {
                console.log(this === obj);
            }
        };
        
        // 第二种方式,对象定义好之后为其附加一个方法(函数表达式)
        obj.test2 = function() {
            console.log(this === obj);
        };
        
        // 第三种方式和第二种方式原理相同
        // 是对象定义好之后为其附加一个方法(函数定义)
        function t() {
            console.log(this === obj);
        }
        obj.test3 = t;
        
        // 这也是为对象附加一个方法函数
        // 但是这个函数绑定了一个不是 obj 的其它对象
        obj.test4 = (function() {
            console.log(this === obj);
        }).bind({});
        
        obj.test();     // true
        obj.test2();    // true
        obj.test3();    // true
        
        // 受 bind() 影响,test4 中的 this 指向不是 obj
        obj.test4();    // false
        
    
  5. 方法中this指向全局对象的情况

    • 这里说的方法中而不是方法调用中。方法中的this指向全局对象,如果不是因为bind(),那就一定不是用的方法调用方式,如下:
        const obj = {
            test() {
                console.log(this === obj);
            }
        };
        
        const t = obj.test;
        t();    // false
    
    • t 就是 obj 的 test 方法,但是 t() 调用时,其中的 this 指向了全局。

    • 提出上面的情况就是为了防止,在将一个对象方法作为回调传递给某个函数之后,却发现运行结果和想的完全不一样的情况发生(忽略了调用方式对this的影响),如下:

    
        class Handlers {
        // 这里 $button 假设是一个指向某个按钮的 jQuery 对象
        constructor(data, $button) {
            this.data = data;
            $button.on("click", this.onButtonClick);
        }
        
        onButtonClick(e) {
            console.log(this.data);
        }
        }
        
        const handlers = new Handlers("string data", $("#someButton"));
        // 对 #someButton 进行点击操作之后
        // 输出 undefined
        // 但预期是输出 string data
        
    
    • this.onButtonClick作为参数传入on()之后,事件触发时,是对函数的直接调用,而不是方法调用,所以其中的this会指向window全局对象
  6. new调用

    • 在es5中,用new调用一个构造函数,会创建一个新对象,而其中的this就指向的这个新对象。
        var data = "Hi";    // 全局变量
        
        function AClass(data) {
        this.data = data;
        }
        
        var a = new AClass("Hello World");
        console.log(a.data);    // Hello World
        console.log(data);      // Hi
        
        var b = new AClass("Hello World");
        console.log(a === b);   // false
        
    
posted @ 2017-02-24 10:19  小敏菇凉  Views(194)  Comments(0Edit  收藏  举报