搜尋

首頁  >  問答  >  主體

了解“this”關鍵字的功能和適當用法

我希望找到關於「this」關鍵字的作用以及如何正確使用它的清晰解釋。

它的行為似乎很奇怪,我不完全明白為什麼。

this如何運作以及何時應該使用它?

P粉155551728P粉155551728441 天前589

全部回覆(2)我來回復

  • P粉087951442

    P粉0879514422023-10-13 14:03:41

    與其他語言相比,this 關鍵字在 JavaScript 中的行為有所不同。在物件導向語言中,this 關鍵字指的是該類別的目前實例。在 JavaScript 中,this 的值由函數的呼叫上下文 (context.function()#) 及其呼叫位置決定。

    <强>1。在全域上下文中使用時

    當您在全域上下文中使用 this 時,它會綁定到全域物件(瀏覽器中的window

    document.write(this);  //[object Window]

    當您在全域上下文中定義的函數內使用 this 時,this 仍然綁定到全域對象,因為該函數實際上是全域上下文的方法。 < /p>

    function f1()
    {
       return this;
    }
    document.write(f1());  //[object Window]

    上面的f1是一個全域物件的方法。因此我們也可以在 window 物件上呼叫它,如下所示:

    function f()
    {
        return this;
    }
    
    document.write(window.f()); //[object Window]

    <强>2。當在物件方法內部使用時

    當您在物件方法中使用 this 關鍵字時,this 會綁定到「直接」封閉物件。

    var obj = {
        name: "obj",
        f: function () {
            return this + ":" + this.name;
        }
    };
    document.write(obj.f());  //[object Object]:obj

    上面我已將「立即」一詞放在雙引號中。這是為了表明,如果將物件嵌套在另一個物件中,則 this 會綁定到直接父物件。

    var obj = {
        name: "obj1",
        nestedobj: {
            name:"nestedobj",
            f: function () {
                return this + ":" + this.name;
            }
        }            
    }
    
    document.write(obj.nestedobj.f()); //[object Object]:nestedobj

    即使你將 function 作為方法明確添加到物件中,它仍然遵循上述規則,即 this 仍然指向直接父物件。

    var obj1 = {
        name: "obj1",
    }
    
    function returnName() {
        return this + ":" + this.name;
    }
    
    obj1.f = returnName; //add method to object
    document.write(obj1.f()); //[object Object]:obj1

    <强>3。呼叫無上下文函數時

    當您在沒有任何上下文(即不在任何物件上)的情況下呼叫的函數內部使用this 時,它會綁定到全域物件(瀏覽器中的window)(即使函數是在物件內部定義的)。

    var context = "global";
    
    var obj = {  
        context: "object",
        method: function () {                  
            function f() {
                var context = "function";
                return this + ":" +this.context; 
            };
            return f(); //invoked without context
        }
    };
    
    document.write(obj.method()); //[object Window]:global

    用函數嘗試一切

    #

    我們也可以用函數來嘗試以上幾點。但還是有些差異。

    • 上面我們使用物件文字表示法為物件新增了成員。我們可以使用this向函數新增成員。來指定它們。
    • 物件字面量表示法創建了一個我們可以立即使用的物件實例。對於函數,我們可能需要先使用 new 運算子來建立其實例。
    • 同樣在物件字面量方法中,我們可以使用點運算子明確地將成員新增到已定義的物件中。這僅會新增到特定實例。不過,我已將變數新增到函數原型中,以便它反映在函數的所有實例中。

    下面我嘗試了上面使用 Object 和 this 所做的所有操作,但首先建立函數而不是直接編寫物件。

    /********************************************************************* 
      1. When you add variable to the function using this keyword, it 
         gets added to the function prototype, thus allowing all function 
         instances to have their own copy of the variables added.
    *********************************************************************/
    function functionDef()
    {
        this.name = "ObjDefinition";
        this.getName = function(){                
            return this+":"+this.name;
        }
    }        
    
    obj1 = new functionDef();
    document.write(obj1.getName() + "
    "); //[object Object]:ObjDefinition /********************************************************************* 2. Members explicitly added to the function protorype also behave as above: all function instances have their own copy of the variable added. *********************************************************************/ functionDef.prototype.version = 1; functionDef.prototype.getVersion = function(){ return "v"+this.version; //see how this.version refers to the //version variable added through //prototype } document.write(obj1.getVersion() + "
    "); //v1 /********************************************************************* 3. Illustrating that the function variables added by both above ways have their own copies across function instances *********************************************************************/ functionDef.prototype.incrementVersion = function(){ this.version = this.version + 1; } var obj2 = new functionDef(); document.write(obj2.getVersion() + "
    "); //v1 obj2.incrementVersion(); //incrementing version in obj2 //does not affect obj1 version document.write(obj2.getVersion() + "
    "); //v2 document.write(obj1.getVersion() + "
    "); //v1 /********************************************************************* 4. `this` keyword refers to the immediate parent object. If you nest the object through function prototype, then `this` inside object refers to the nested object not the function instance *********************************************************************/ functionDef.prototype.nestedObj = { name: 'nestedObj', getName1 : function(){ return this+":"+this.name; } }; document.write(obj2.nestedObj.getName1() + "
    "); //[object Object]:nestedObj /********************************************************************* 5. If the method is on an object's prototype chain, `this` refers to the object the method was called on, as if the method was on the object. *********************************************************************/ var ProtoObj = { fun: function () { return this.a } }; var obj3 = Object.create(ProtoObj); //creating an object setting ProtoObj //as its prototype obj3.a = 999; //adding instance member to obj3 document.write(obj3.fun()+"
    ");//999 //calling obj3.fun() makes //ProtoObj.fun() to access obj3.a as //if fun() is defined on obj3

    <强>4。在建構函數內部使用時。

    當函數用作建構函數時(即使用 new 關鍵字呼叫時),函數體內的 this 指向正在建構的新物件。 < /p>

    var myname = "global context";
    function SimpleFun()
    {
        this.myname = "simple function";
    }
    
    var obj1 = new SimpleFun(); //adds myname to obj1
    //1. `new` causes `this` inside the SimpleFun() to point to the
    //   object being constructed thus adding any member
    //   created inside SimipleFun() using this.membername to the
    //   object being constructed
    //2. And by default `new` makes function to return newly 
    //   constructed object if no explicit return value is specified
    
    document.write(obj1.myname); //simple function

    <强>5。當原型鏈上定義的函數內部使用時

    #

    如果該方法位於物件的原型鏈上,則該方法內的 this 引用該方法被呼叫的對象,就像該方法是在該對像上定義的一樣。

    var ProtoObj = {
        fun: function () {
            return this.a;
        }
    };
    //Object.create() creates object with ProtoObj as its
    //prototype and assigns it to obj3, thus making fun() 
    //to be the method on its prototype chain
    
    var obj3 = Object.create(ProtoObj);
    obj3.a = 999;
    document.write(obj3.fun()); //999
    
    //Notice that fun() is defined on obj3's prototype but 
    //`this.a` inside fun() retrieves obj3.a

    <强>6。內部call()、apply()和bind()函數

    #
    • 所有這些方法均在 Function.prototype 上定義。
    • 這些方法允許編寫一次函數並在不同的上下文中呼叫它。換句話說,它們允許指定在執行函數時將使用的 this 值。它們還可以在呼叫原始函數時將任何參數傳遞給原始函數。
    • fun.apply(obj1 [, argsArray])obj1 設為this 內的值code>fun() 並呼叫fun(),傳遞argsArray 的元素作為其參數。
    • fun.call(obj1 [, arg1 [, arg2 [,arg3 [, ...]]]]) - 設定obj1 為fun () 內部this 的值,並呼叫fun() 傳遞arg1, arg2, arg3, ...< /code> 作為其參數。
    • fun.bind(obj1 [, arg1 [, arg2 [,arg3 [, ...]]]]) - 傳回對函數的參考fun,其中fun 內的this 綁定到obj1,並且fun 的參數綁定到指定的參數arg1、arg2,arg3, ....
    • 現在,applycallbind 之間的差異一定已經很明顯了。 apply 允許將參數指定為類似數組的對象,即具有數字 length 屬性和相應的非負整數屬性的對象。而 call 允許直接指定函數的參數。 applycall 都會立即在指定上下文中並使用指定參數呼叫函數。另一方面,bind 只是傳回綁定到指定 this 值和參數的函數。我們可以透過將返回函數指派給變數來捕獲對此返回函數的引用,然後我們可以隨時呼叫它。
    function add(inc1, inc2)
    {
        return this.a + inc1 + inc2;
    }
    
    var o = { a : 4 };
    document.write(add.call(o, 5, 6)+"
    "); //15 //above add.call(o,5,6) sets `this` inside //add() to `o` and calls add() resulting: // this.a + inc1 + inc2 = // `o.a` i.e. 4 + 5 + 6 = 15 document.write(add.apply(o, [5, 6]) + "
    "); //15 // `o.a` i.e. 4 + 5 + 6 = 15 var g = add.bind(o, 5, 6); //g: `o.a` i.e. 4 + 5 + 6 document.write(g()+"
    "); //15 var h = add.bind(o, 5); //h: `o.a` i.e. 4 + 5 + ? document.write(h(6) + "
    "); //15 // 4 + 5 + 6 = 15 document.write(h() + "
    "); //NaN //no parameter is passed to h() //thus inc2 inside add() is `undefined` //4 + 5 + undefined = NaN

    7.事件處理程序內的 this

    • 當您將函數直接指派給元素的事件處理程序時,直接在事件處理函數內使用 this 來引用對應的元素。這種直接的函數分配可以使用 addeventListener 方法或透過 onclick 等傳統事件註冊方法來完成。
    • 同樣,當您直接在元素的事件屬性(如
    • 但是,透過在事件處理函數或事件屬性內呼叫的其他函數間接使用 this 會解析為全域物件 window
    • 當我們使用 Microsoft 的事件註冊模型方法 attachEvent 將函數附加到事件處理程序時,可以實現與上述相同的行為。它不是將函數分配給事件處理程序(從而創建元素的函數方法),而是在事件上調用函數(在全局上下文中有效地調用它)。

    我建議在 JSFiddle.

    #
    sssccc
    
    

    Using `this` "directly" inside event handler or event property



    Using `this` "indirectly" inside event handler or event property



    IE only:

    <强>8。 ES6 箭頭函數中的 this

    在箭頭函數中,this 的行為類似於公共變數:它將從其詞法範圍繼承。定義箭頭函數的函數的 this 將是箭頭函數的 this

    所以,這與以下行為相同:

    (function(){}).bind(this)

    請參閱以下程式碼:

    const globalArrowFunction = () => {
      return this;
    };
    
    console.log(globalArrowFunction()); //window
    
    const contextObject = {
      method1: () => {return this},
      method2: function(){
        return () => {return this};
      }
    };
    
    console.log(contextObject.method1()); //window
    
    const contextLessFunction = contextObject.method1;
    
    console.log(contextLessFunction()); //window
    
    console.log(contextObject.method2()()) //contextObject
    
    const innerArrowFunction = contextObject.method2();
    
    console.log(innerArrowFunction()); //contextObject 
    

    回覆
    0
  • P粉156532706

    P粉1565327062023-10-13 11:53:32

    是JavaScript 中的關鍵字是執行上下文的屬性。它的主要用途是在函數和構造函數中。 this 的規則非常簡單(如果您堅持最佳實踐)。

    規格中this的技術描述

    ECMAScript 標準定義this 透過抽像操作(縮寫為AOResolveThisBinding

    全球環境記錄模組環境記錄,以及 函數環境記錄每個都有自己的 GetThisBinding 方法。

    GetThisEnvironment AO 尋找目前的執行執行上下文的LexicalEnvironment 並尋找最接近的上升環境記錄(透過迭代存取其[[OuterEnv]] 屬性),該記錄具有this< /em> 綁定(即HasThisBinding 傳回true)。此過程以三種環境記錄類型之一結束。

    this 的值通常取決於程式碼是否處於 嚴格模式

    GetThisBinding 的傳回值反映目前執行上下文的 this 值,因此每當建立新的執行上下文時,this 都會解析為不同的值。噹噹前執行上下文被修改時,也會發生這種情況。以下小節列出了可能發生這種情況的五種情況。

    您可以將程式碼範例放入 AST 資源管理器中,以遵循規範詳細資訊。

    1。腳本中的全域執行上下文

    這是在頂層評估的腳本程式碼,例如直接在

    當在腳本的初始全域執行上下文中時,評估 this 會導致 GetThisBinding 採取以下步驟:

    全域環境記錄的[[GlobalThisValue]] 屬性總是設定為主機定義的全域物件,可透過globalThis ( Web 上的window,Node.js 上的globalMDN 上的文件)。請按照 InitializeHostDefinedRealm 的步驟來了解 [[GlobalThisValue]] 屬性是如何產生的。

    2。 模組

    中的全域執行上下文

    ECMAScript 2015 中引進了模組。

    這適用於模組,例如直接位於