天天看點

JavaScript中各種情況下this的指向問題(全面總結篇)

一、this–>undefined

ES6的嚴格模式中,如果調用函數中的this或者頂層this将會指向undefined。

<script type="module">
      function fn(){
          console.log(this);
      }
      fn(); //undefined
</script>
           
<script type="module">
      console.log(this); //undefined
</script>
           

二、this–>window

  1. 非嚴格模式中函數或者頂層中調用this,會被指向window
  2. 回調函數中預設this的指向是window
  3. 當使用call、apply、bind時,如果帶入的第一個參數是null或者undefined,this預設會被指向window

三、this–>上下文環境中的this

  1. 箭頭函數
var obj={
          a:function(){
              setTimeout(()=>{
                  this.b();//this就是目前箭頭函數外的上下文環境中this的指向
                  // setTimeout外面的this是什麼這個箭頭函數中的this就是什麼
              },500)
              document.addEventListener("click",e=>{
                  this.clickhandler(e)
                  // 這裡的this指向addEventListener函數外上下文環境中this的指向
              });
          },
          b:function(){
              console.log("aaa");
          },
          clickhandler:function(e){

          }
      }
           
  1. 對象的屬性中this也是上下環境中this的指向
var c = 20;
      var obj = {
        c: 10,
        a: this.c,
        b: function () {
          console.log(this.a);
        },
      };
      obj.b(); //20
           
var a = 100;
      var obj = {
        a: 10,
        c: 50,
        b: {
          a: 0,
          c: this.a,
          run: function () {
            console.log(this.c);
          },
        },
      };

      obj.b.run(); //100
           
var a = 100;
      document.a = -100;
      document.addEventListener("click", clickHandler);
      function clickHandler(e) {
        // this ---> document
        var obj = {
          a: 10,
          c: 50,
          b: {
            a: 0,
            c: this.a,
            run: function () {
              console.log(this.c);
            },
          },
        };
        obj.b.run(); //-100
      }
           

四、this—>函數中的arguments

在函數中使用回調函數時,利用arguments的參數來執行回調函數,被執行的回調函數中this指向的是目前函數arguments.callee.caller函數的arguments。

// fn中的this是fn1中的arguments
      var a;
      function fn() {
        console.log(this);
        console.log(this === a);
      }
      function fn1(f) {
        a = arguments;
        arguments[0]();
      }

      fn1(fn);
           

上面代碼的列印結果如下圖:

JavaScript中各種情況下this的指向問題(全面總結篇)
var o={
          a:function(){
              console.log(this); //Arguments [ƒ, callee: ƒ, Symbol(Symbol.iterator): ƒ]
          },
          b:function(f){
              arguments[0]();
          },
          c:function(){
              this.b(this.a)
          }
      }
      o.c();
           

上面代碼的列印結果如下圖:

JavaScript中各種情況下this的指向問題(全面總結篇)

五、事件中的this—>事件偵聽的對象 e.currentTarget

document.addEventListener("click", clickHandler);
      function clickHandler(e) {
        console.log(this); //document
      }
           

六、this被call、apply和bind重新指定為對應的對象

function fn() {
        console.log(this);
      }
      fn.call({ a: 1 }); //{a: 1}
      fn.apply({ a: 1 }); //{a: 1}
      fn.bind({ a: 1 })(); //{a: 1}
           

七、對象函數中的this–>目前對象

var obj={
          a:1,
          b:function(){
              console.log(this); //目前對象
          }
      }
      obj.b(); //{a: 1, b: ƒ}
           

八、ES6類中的this–>目前類的執行個體化對象

  1. 在執行個體化方法中或者構造函數中調用的this都是目前類的執行個體化的對象
  2. 在靜态方法中,this就是目前類名或者目前類的構造函數
class Box {
        constructor() {
          console.log("aaa");
        }
        // 執行個體化方法
        play() {
          console.log(this); //this是目前類執行個體化的對象
        }
        // 靜态方法
        static run() {
          console.log(this); //this是目前類也是構造函數
          console.log("aaaa");
        }
      }

	  // 當直接調用Box時Box指的是類,當使用new Box()時Box指的是類裡面的構造函數constructor
	  var b=new Box();
      b.constructor.run();
      Box.run();
      console.log(b.constructor);
      console.log(b.constructor===Box);
           

注意:隻有執行個體化對象才有屬性constructor。

九、ES5類中的this

function Box() {
        console.log("aaa");
        
        if (!(this instanceof Box)) {
          return new Box();
        }
      }
      
      // 相當于ES6類中的執行個體化方法
      Box.prototype.play = function () {
        console.log(this); //執行個體化的對象
      };
      
      // 相當于ES6中的靜态方法
      Box.run = function () {
        console.log(this); //就是目前類
      };
      
      // Box是類也是構造函數
      
      var b = new Box(); //aaa
      //當執行執行個體Box時會執行Box函數,并且自動傳回Box的一個執行個體化元素
      
      var c = Box(); //列印兩次aaa
      //在ES5中如果不使用new來執行構造函數,構造函數自身也是可以獨自執行的,但是不能傳回執行個體化對象
      
      console.log(b, c); //列印兩個 Box {}
           
  1. 通過new執行個體目前Box時,this就是目前執行個體化的對象。
  2. 如果直接調用函數Box,this就是window或者undefined(嚴格模式下)。

關于ES5和ES6中類的拓展知識:

  1. 在ES5或者ES6中,類僅允許有且僅有一個構造函數,并且該函數的名稱和類名相同。
  2. 在ES6中會将構造函數描述成constructor;在ES5中會将構造函數寫成類名。但是不管是ES5還是ES6,執行個體化對象的constructor都是構造函數。
  3. 構造函數中不允許使用return傳回資料。

小練習:

var obj = {
        a: 10,
        abc: function () {
          setTimeout(() => {
            this.a++;
            console.log(this.a, "_______"); //11
            (function () {
              this.a++;
              console.log(this.a); //21
            }.call(this.c));
          }, 1000);
        },
        c: {
          a: 20,
        },
      };

      obj.abc();
           

繼續閱讀