浅谈this的一些规律

170 阅读3分钟

this在面试的时候是最常见的考点之一,也是我们必须要掌握的重要知识点,但是按照MDN上的描述:

this的值是当前执行上下文(global、function 或 eval)的一个属性,在非严格模式下,总是指向一个对象,在严格模式下可以是任意值。

那么然后我来简单的聊一下this在浏览器中的一些常见情况:

  1. 事件绑定

    • 不论是DOM0还是DOM2级事件绑定,给元素ele的某个事件行为绑定方法,当事件触发执行,方法中的this是当前元素ele本身
        document.body.onclick = function(){
            console.log(this) //body
        }
        
        document.body.addEventListener('click',function(){
            console.log(this)  //body
        })
    
    • 特殊情况

      • IE6~8中给予attachEvent实现DOM2级事件绑定,事件触发方法执行,方法中的this不再是元素本身,大部分情况都是window,但是现在大部分企业已经不再兼容IE6~8,所以次情况可以忽略。

      • 如果基于call/apply/bind强制改变函数中的this,我们都是以强制改变为主

  2. 普通函数

    • 函数执行,看函数前是否有“点(.)”,如果有,那么“点”前边是什么this就表示什么,如果“点”前边什么都没有,this就表示window(非严格模式),严格模式下表示undefined
    var a = 1;
    function fn(){
        console.log(this.a)
    }
    var obj = {
        a:2,
        fn
    }
    var obj2 = {
        a:3,
        obj,
        fn
    }
    fn()     //1
    
    obj.fn()    //2
    
    var f = obj.fn;
    f()    //1
    
    obj2.obj.fn();   // 2
    obj2.fn();   //3
    
    • 自执行函数,其内的this一般都是表示window(非严格模式)/undefined(严格模式),哪怕自执行函数写在对象哪依然如此
    (function(){
        console.log(this) //window
    })()
    
    var obj = {
        fn: (function(){
            console.log(this) //window
            return 1
        })()
    }
    
    • 回调函数中的this一般表示window(非严格模式)/undefined(严格模式),除非做过特殊处理
      • 回调函数: 将一个函数a作为值(参数),传递给另一个函数b,当b执行的过程中,把传递进来的函数a执行...
        setTimeout(function(){
            console.log(this)  //window
        },5000)
        
        var arr = [1];
        var obj = {a: 1}
        arr.forEach(function(){
            console.log(this) //window
        })
        
        arr.forEach(function(){
            console.log(this) //obj
        }, obj)
    
    • 如果基于call/apply/bind强制改变函数中的this,我们都是以强制改变为主
     var a = 1;
     function fn(){
         console.log(this.a)
     }
     var obj = {
         a:2,
         fn
     }
     
     var obj2 = {
         a:3,
         obj,
         fn
     }
     
     obj.fn.bind(obj2)()    //3
    
  3. new

    首先来看一下new的过程中发生了什么:

    1. 创建一个空的简单JavaScript对象(即 {} );
    2. 为步骤1新创建的对象添加属性 proto ,将该属性链接至构造函数的原型对象 ;
    3. 将步骤1新创建的对象作为this的上下文 ;
    4. 如果该函数没有返回对象,则返回this

    通过上方我们可以看出,关于this最关键的是第三四点

    • 如果函数 constructor 里没有返回对象的话,this 指向的是 new 之后得到的实例
    function Foo(a){
        this.a = a  //这里this指向f
        return 1
    }
    var f = new Foo(2)
    f.a //2
    
    • 如果函数 constructor 里有返回对象的话,this 指向的是返回的对象
    function Foo(a){
        console.log(this)
        this.a = a   //这里this指向{a:10}
        return {
            a: 100
        }
    }
    var f = new Foo(2)
    f.a //100
    
  4. 箭头函数

    • 箭头函数本身没有this,其this完全继承上一级,上一级的this是什么,这里就会继承什么
    function fn() {
      return {
        b: () => {
          console.log(this)
        }
      }
    }
    
    fn().b() // window
    fn().b.bind(1)() // window
    fn.bind(2)().b.bind(3)() // Number {2}
    
    1. 总结

    this取什么值是在函数执行的时候确定的,而不是在函数定义的时候确定的,在看this的取值的时候,关注相关函数的执行就好。

    • 事件绑定,指向触发事件的元素(参考上方第1点)
    • 当作普通函数被调用,指向window(参考上方第2点)
    • 作为对象方法使用,指向对象本身(参考上方第2点)
    • 使用call、bind、apply,看传入的参数(参考上方第2点)
    • class中使用,大部分指向实例本身(这需要看一下上方的new相关部分上方第3点)
    • 箭头函数,找其上一层作用域中的this(参考上方第4点)