对this的简单理解

  • this的绑定时间

              var length=10;
                  function f(){
                      var length=20;
                      console.log(this.length);
                  }
                  f();   //10

    ​ 我们以这个简单的代码块为例子,可以看到this在方法f里面,那么this在什么时候绑定呢?顺着代码来,可以看到,我们这里是一个声明的函数方法(函数定义的三种方法之一)。先声明,然后分配地址空间(初始化),再定义函数体,那么this是在定义函数体后就绑定了吗,看结果,显然不是,那么this只会是在被调用的时候才绑定的。

    ​ 我们接着深度剖析一下,为什么是在调用时也就是运行时绑定的呢?

    ​ 我们知道JS是一个脚本语言,很多东西都是简化的,JS在声明函数方面,把构造函数/方法/lambda/全局函数等隐式地混合在一起了。

    ​ 我们这里要知道——当声明方法时需要找到对应的构造函数,让这个方法内部的 this 指向构造函数生成的实例;当声明构造函数的时候,this 则指向构造函数生成的这个实例;当声明 lambda 表达式的时候,不存在 this 指向,这时候 this 应按照闭包的规程逐次向上层作用域找,没找到则抛出异常

    ​ 在函数声明的时候,JS是不能通过静态分析去确定这到底是构造函数还是lambda等等,所以只能在动态调用方法时去绑定this。

    ​ 额外提一点,在lambda方面,JS在ES6也是引入了箭头函数,有兴趣的可以了解一下。

  • this的绑定方式

    ​ 一共有四种绑定方式(优先级从低到高),我们一一介绍。

    1、默认绑定

    ​ 我们知道了this是在调用时绑定的,那么绑定谁呢也就是说指向谁呢?具体的在下一部分讲。

    ​ 很容易理解,默认绑定就是说this会有一个默认的绑定对象。

    2、隐式绑定

    ​ 这里也容易理解,this隐式地改变了原来的默认绑定。简单来说,就是偷偷地改变了绑定对象,不再是默认的。

    3、显式绑定

    ​ 既然是显式的,那么就不再是偷偷的了,而是明目张胆地用一些方法去改变this绑定的对象。那么是哪些方法呢,这里简单介绍一下:call,apply,bind。

  var a={
          name:"shan",
          age:21,
          sex:"male"
      }
  var b={
      name:"sun",
      getName:function (){
          console.log("name:"+this.name+
              "  age:"+this.age);
      }
  }

  b.getName.call(a);   //name:shan  age:21

​ 可以看到call把函数里this的绑定对象从b转到了a,并且执行了该函数。call和apply的区别就是,call后面的参数是一个参数队列,apply后面则是合在一起的一个数组;至于bind,它和call的区别就是不会执行函数,而是返回一个新函数对象。

4、new绑定

​ new关键字是用来实例化的,this自然是指向实例化后的实例对象。

      class gn{
          name="shan";
          constructor() {
              console.log(this.name);
          }
      }
      var b=new gn();      //shan
      console.log(b.name); //shan

​ 其实new关键字的实现原理也是涉及到了显式绑定方法,有兴趣的可以去了解一下。

  • this的指向

    1、函数声明
        function f(){
            var length=100;
            console.log(this.length);
        }
        f();   //undefined

​ 无对象调用时,this是一个默认绑定,指向的是默认的全局对象也就是window,还有下面这种情况:

      var length=10;
      function f(){
          var length=100;
          console.log(this.length);
      }
      f();   //10

​ 这里涉及了一个数据声明的问题,有兴趣的可以去学习一下。

2、有对象调用
      var a={
          name:"shan",
          getName:function(){
              console.log(this.name);
          }
      }
      a.getName();   //shan

​ 有对象调用时,this隐式绑定了该对象。所以指向该对象,如上,返回的是shan。如下,可以对比一下:

      var name="dan";
     var a={
          getName:function(){
              console.log(this.name);
          }
      }
      a.getName();    //undefined

​ 可见,this绑定的不再是默认的window对象。

3、函数表达式
      var name="shan";
      var a=function(){
          console.log(this.name)
      }
      a();

​ 这个也是无对象调用,所以是默认绑定,指向window。

4、实例化调用

​ 这里参考上方的new绑定,this指向的就是实例化后的对象

      var a=function(){
          this.name="shan";
          console.log(this.name);
      }
      var b=new a();            //shan
      console.log(b.name);    //shan
      b.name="dan";
      console.log(b.name);    //dan
5、call、apply调用

​ 这里参考上方的显式绑定,call、apply其实是一种上下文绑定

    var a={
        name:"shan",
        age:21,
        sex:"male"
    }
    var b={
        name:"sun",
        getName:function (){
            console.log("name:"+this.name+
                "  age:"+this.age);
        }
    }
    b.getNmae.apply(a);   //name:shan  age:21
6、箭头函数
var a={
    name:"dan",
    getName:function (){
        setTimeout(()=>console.log(this.name),100)
    }
}
a.getName();

​ 箭头函数本身是没有this关键字的,但是它会继承父环境,在这里体现出来的就是它继承了getName函数的this,指向了a对象。