首頁  >  問答  >  主體

如何在回調函數中正確存取`this`對象

<p>我有一個建構函數,它註冊了一個事件處理程序:</p> <p><br /></p> <pre class="snippet-code-js lang-js prettyprint-override"><code>function MyConstructor(data, transport) { this.data = data; transport.on('data', function () { alert(this.data); }); } // 模擬傳輸對象 var transport = { on: function(event, callback) { setTimeout(callback, 1000); } }; // 呼叫方式 var obj = new MyConstructor('foo', transport);</code></pre> <p><br /></p> <p>然而,在回呼函數中我無法存取創建的物件的<code>data</code>屬性。看起來<code>this</code>並不是指向被創建的對象,而是指向另一個對象。 </p> <p>我還嘗試使用物件方法而不是匿名函數:</p> <pre class="brush:php;toolbar:false;">function MyConstructor(data, transport) { this.data = data; transport.on('data', this.alert); } MyConstructor.prototype.alert = function() { alert(this.name); };</pre> <p>但它也出現了相同的問題。 </p> <p>如何存取正確的物件? </p>
P粉608647033P粉608647033398 天前494

全部回覆(2)我來回復

  • P粉921130067

    P粉9211300672023-08-21 14:13:52

    以下是在子上下文中存取父上下文的幾種方法:

    1. 您可以使用bind()函數。
    2. 在另一個變數中儲存上下文/this的引用(請參閱下面的範例)。
    3. 使用ES6的箭頭函數。
    4. 修改程式碼、函數設計和架構 - 為此,您應該掌握JavaScript中的設計模式

    1. 使用bind()函數

    function MyConstructor(data, transport) {
        this.data = data;
        transport.on('data', ( function () {
            alert(this.data);
        }).bind(this) );
    }
    // 模拟传输对象
    var transport = {
        on: function(event, callback) {
            setTimeout(callback, 1000);
        }
    };
    // 调用方式
    var obj = new MyConstructor('foo', transport);
    

    如果您使用Underscore.js - http://underscorejs.org/#bind

    #
    transport.on('data', _.bind(function () {
        alert(this.data);
    }, this));

    2. 將上下文/this的引用儲存在另一個變數中

    function MyConstructor(data, transport) {
      var self = this;
      this.data = data;
      transport.on('data', function() {
        alert(self.data);
      });
    }
    

    3. 箭函數

    function MyConstructor(data, transport) {
      this.data = data;
      transport.on('data', () => {
        alert(this.data);
      });
    }
    

    回覆
    0
  • P粉176151589

    P粉1761515892023-08-21 10:04:21

    關於 this 的知識

    this(也稱為「上下文」)是每個函數內部的特殊關鍵字,其值只取決於函數的呼叫方式,而不是定義方式。它不受詞法作用域的影響,與其他變數不同(除了箭頭函數,見下文)。以下是一些範例:

    function foo() {
        console.log(this);
    }
    
    // 普通函数调用
    foo(); // `this` 将引用 `window`
    
    // 作为对象方法
    var obj = {bar: foo};
    obj.bar(); // `this` 将引用 `obj`
    
    // 作为构造函数
    new foo(); // `this` 将引用从 `foo.prototype` 继承的对象

    要了解更多關於 this 的知識,請參閱MDN文件


    如何引用正確的 this

    #使用 箭頭函數

    #ECMAScript 6 引入了箭頭函數,可以將其視為lambda函數。它們沒有自己的this綁定。相反,this在作用域中查找,就像普通變數一樣。這意味著你不需要呼叫.bind。它們還具有其他特殊行為,請參閱MDN文件以了解更多資訊。

    function MyConstructor(data, transport) {
        this.data = data;
        transport.on('data', () => alert(this.data));
    }

    不使用 this

    #實際上,你不需要特別訪問this,而是需要存取它所指的物件。因此,一個簡單的解決方案是建立一個新變量,也指向該物件。變數可以有任何名稱,但常見的是selfthat

    function MyConstructor(data, transport) {
        this.data = data;
        var self = this;
        transport.on('data', function() {
            alert(self.data);
        });
    }

    由於self是一個普通變量,它遵循詞法作用域規則,並且可以在回調函數內部存取。這還有一個優點,就是可以存取回呼函數本身的this值。

    明確設定回呼函數的this - 第一部分

    看起來你無法控制this的值,因為它的值是自動設定的,但實際上並非如此。

    每個函數都有一個方法.bind [文件],它會傳回一個會this綁定到一個值的新函數。該函數的行為與你呼叫.bind時的函數完全相同,只是this由你設定。無論如何或何時呼叫該函數,this將始終引用傳遞的值。

    function MyConstructor(data, transport) {
        this.data = data;
        var boundFunction = (function() { // 括号不是必需的
            alert(this.data);             // 但可能提高可读性
        }).bind(this); // <- 这里我们调用了`.bind()`
        transport.on('data', boundFunction);
    }

    在這種情況下,我們將回呼函數的this綁定到MyConstructorthis值。

    注意:對於jQuery的綁定上下文,使用jQuery.proxy [文件]。這樣做的原因是在解綁事件回呼時不需要儲存函數的參考。 jQuery在內部處理這個。

    設定回呼函數的this - 第二部分

    一些接受回呼函數的函數/方法也接受一個值,該值應該作為回呼函數的this。這與手動綁定相同,但是函數/方法會為你執行綁定。例如,Array#map [文檔]就是這樣的方法。它的簽名是:

    array.map(callback[, thisArg])

    第一個參數是回呼函數,第二個參數是this應該要引用的值。以下是一個人為的範例:

    var arr = [1, 2, 3];
    var obj = {multiplier: 42};
    
    var new_arr = arr.map(function(v) {
        return v * this.multiplier;
    }, obj); // <- 这里我们将`obj`作为第二个参数传递

    注意:是否可以傳遞this的值通常在函數/方法的文件中提到。例如,jQuery的$.ajax方法[文件]描述了一個名為context的選項:


    常見問題:將物件方法用作回呼/事件處理程序

    這個問題的另一個常見表現形式是將物件方法用作回調/事件處理程序。在JavaScript中,函數是一等公民,術語「方法」只是指一個作為物件屬性值的函數。但是該函數與其「包含」物件之間沒有特定的連結。

    考慮以下範例:

    function Foo() {
        this.data = 42,
        document.body.onclick = this.method;
    }
    
    Foo.prototype.method = function() {
        console.log(this.data);
    };

    函數this.method被指派為點擊事件處理程序,但如果點擊document.body,記錄的值將是undefined,因為在事件處理程序內部,this指的是document.body,而不是Foo的實例。
    如前所述,this引用的是函數如何呼叫,而不是如何定義
    如果程式碼如下所示,可能更明顯函數沒有隱式引用物件:

    function method() {
        console.log(this.data);
    }
    
    
    function Foo() {
        this.data = 42,
        document.body.onclick = this.method;
    }
    
    Foo.prototype.method = method;

    解決方案與上面提到的相同:如果可用,使用.bindthis明確綁定到特定值

    document.body.onclick = this.method.bind(this);

    或透過使用匿名函數作為回調/事件

    回覆
    0
  • 取消回覆