首頁 > 軟體

JavaScript 執行上下文的視角詳解this使用

2023-02-28 18:01:53

前言

在物件內部的方法中使用物件內部的屬性是一個非常普遍的需求。但是 JavaScript 的作用域機制並不支援這一點,基於這個需求,JavaScript 有另外一套 this 機制。

this 是和執行上下文繫結的,也就是說每個執行上下文中都有一個 this。執行上下文主要分為三種——全域性執行上下文、函數執行上下文和 eval 執行上下文,所以對應的 this 也只有這三種——全域性執行上下文中的 this、函數中的 this 和 eval 中的 this。

全域性執行上下文中的 this

全域性執行上下文中的 this 是指向 window 物件的。這也是 this 和作用域鏈的 唯一交點,作用域鏈的最底端包含了 window 物件,全域性執行上下文中的 this 也是指向 window 物件。

函數執行上下文中的 this

function foo() {
  console.log(this);
}
foo();

執行這段程式碼,列印出來的也是 window 物件,這說明在預設情況下呼叫一個函數,其執行上下文中的 this 也是指向 window 物件的。

通常情況下,有下面三種方式來設定函數執行上下文中的 this 值:

1. 通過函數的 call 方法設定

let bar = {
  myName: "極客邦",
  test1: 1,
};
function foo() {
  this.myName = "極客時間";
}
foo.call(bar);
console.log(bar);
console.log(myName);

執行這段程式碼,你就能發現 foo 函數內部的 this 已經指向了 bar 物件,因為通過列印 bar 物件,可以看出 barmyName 屬性已經由“極客邦”變為“極客時間”了,同時在全域性執行上下文中列印 myName,JavaScript 引擎提示該變數未定義。其實除了 call 方法,你還可以使用 bindapply 方法來設定函數執行上下文中的 this

2. 通過物件呼叫方法設定

var myObj = {
  name: "極客時間",
  showThis: function () {
    console.log(this);
  },
};
myObj.showThis();

執行這段程式碼,你可以看到,最終輸出的 this 值是指向 myObj 的。所以,你可以得出這樣的結論:使用物件來呼叫其內部的一個方法,該方法的 this 是指向物件本身的。

接下來我們稍微改變下呼叫方式,把 showThis 賦給一個全域性物件,然後再呼叫該物件,程式碼如下所示:

var myObj = {
  name: "極客時間",
  showThis: function () {
    this.name = "極客邦";
    console.log(this);
  },
};
var foo = myObj.showThis;
foo();

執行這段程式碼,你會發現 this 又指向了全域性 window 物件。

結論:

在全域性環境中呼叫一個函數,函數內部的 this 指向的是全域性變數 window

通過一個物件來呼叫其內部的一個方法,該方法的執行上下文中的 this 指向物件本身。

3. 通過建構函式中設定

function CreateObj() {
  this.name = "極客時間";
}
var myObj = new CreateObj();

當執行 new CreateObj() 的時候,JavaScript 引擎做了如下四件事:

首先建立了一個空物件 tempObj

接著呼叫 CreateObj.call 方法,並將 tempObj 作為 call 方法的引數,這樣當 CreateObj 的執行上下文建立時,它的 this 就指向了 tempObj 物件

然後執行 CreateObj 函數,此時的 CreateObj 函數執行上下文中的 this 指向了 tempObj 物件

最後返回 tempObj 物件

var tempObj = {};
CreateObj.call(tempObj);
return tempObj;

這樣,就通過 new 關鍵字構建好了一個新物件,並且建構函式中的 this 其實就是新物件本身。

this 的設計缺陷以及應對方案

1. 巢狀函數中的 this 不會從外層函數中繼承

var myObj = {
  name: "極客時間",
  showThis: function () {
    console.log(this);
    function bar() {
      console.log(this);
    }
    bar();
  },
};
myObj.showThis();

執行這段程式碼後,會發現函數 bar 中的 this 指向的是全域性 window 物件,而函數 showThis 中的 this 指向的是 myObj 物件。

可以通過一個小技巧來解決這個問題,比如在 showThis 函數中宣告一個變數 that 用來儲存 this,然後在 bar 函數中使用 that。其實,這個方法的的本質是把 this 體系轉換為了作用域的體系。

其實,你也可以使用 ES6 中的箭頭函數來解決這個問題:

var myObj = {
  name: "極客時間",
  showThis: function () {
    console.log(this);
    var bar = () => {
      console.log(this);
    };
    bar();
  },
};
myObj.showThis();

這是因為 ES6 中的箭頭函數並不會建立其自身的執行上下文,所以箭頭函數中的 this 取決於它的外部函數。

2. 普通函數中的 this 預設指向全域性物件 window

在實際工作中,我們並不希望函數執行上下文中的 this 預設指向全域性物件,因為這樣會打破資料的邊界,造成一些誤操作。如果要讓函數執行上下文中的 this 指向某個物件,最好的方式是通過 call 方法來顯示呼叫。

可以通過設定 JavaScript 的 嚴格模式 來解決(在第一行加上 "use strict";)。在嚴格模式下,預設執行一個函數,其函數的執行上下文中的 this 值是 undefined

以上就是JavaScript 執行上下文的視角詳解this使用的詳細內容,更多關於JavaScript 執行上下文 this的資料請關注it145.com其它相關文章!


IT145.com E-mail:sddin#qq.com