Home  >  Q&A  >  body text

How to correctly access `this` object in callback function

<p>I have a constructor that registers an event handler: </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); }); } //Simulate transfer object var transport = { on: function(event, callback) { setTimeout(callback, 1000); } }; // Calling method var obj = new MyConstructor('foo', transport);</code></pre> <p><br /></p> <p>However, within the callback function I cannot access the <code>data</code> properties of the created object. It seems that <code>this</code> does not point to the created object, but to another object. </p> <p>I also tried using object methods instead of anonymous functions: </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>But it also had the same problem. </p> <p>How do I access the correct object? </p>
P粉608647033P粉608647033398 days ago492

reply all(2)I'll reply

  • P粉921130067

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

    The following are several ways to access the parent context in the child context:

    1. You can use the bind() function.
    2. Store a reference to context/this in another variable (see example below).
    3. Use ES6's arrow function.
    4. Modify code, function design, and architecture - To do this, you should master Design Patterns in JavaScript.

    1. Use bind()function

    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);
    

    If you use Underscore.js - http://underscorejs.org/#bind

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

    2. Store the reference of context/this in another variable

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

    3. Arrow function

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

    reply
    0
  • P粉176151589

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

    Knowledge about this

    this (also called "context") is a special keyword inside every function whose value depends only on how the function is called, not how it is defined. It is not affected by lexical scope, unlike other variables (except arrow functions, see below). Here are some examples:

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

    To learn more about this, please see the MDN documentation.


    How to quote the correct this

    Use arrow function

    ECMAScript 6 introduced arrow functions, which can be thought of as lambda functions. They do not have their own this binding. Instead, this is looked up in the scope, just like a normal variable. This means you don't need to call .bind. They also have other special behaviors, see the MDN documentation for more information.

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

    Do not use this

    Actually, you don't need to access this specifically, but rather the object it refers to. So a simple solution is to create a new variable that also points to the object. Variables can have any name, but common ones are self and that.

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

    Since self is a normal variable, it follows lexical scoping rules and can be accessed inside the callback function. This also has the advantage of having access to the this value of the callback function itself.

    Explicitly setting the callback functionthis - Part 1

    It may seem like you have no control over the value of this since its value is set automatically, but that's not actually the case.

    Each function has a method .bind [Documentation], which returns a method that will thisNew function bound to a value. This function behaves exactly like when you call .bind, except that this is set by you. No matter how or when the function is called, this will always refer to the passed value.

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

    In this case, we bind the this of the callback function to the this value of MyConstructor.

    Note: For jQuery's binding context, use jQuery.proxy [Documentation]. The reason for this is that there is no need to store a reference to the function when unbinding the event callback. jQuery handles this internally.

    Setting the callback functionthis - Part 2

    Some functions/methods that accept a callback function also accept a value that should be used as this of the callback function. This is the same as manual binding, but the function/method performs the binding for you. For example, Array#map [Documentation] is such a method. Its signature is:

    array.map(callback[, thisArg])

    The first parameter is the callback function, and the second parameter is the value that this should reference. Here's a contrived example:

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

    Note: Whether the value of this can be passed is usually mentioned in the documentation of the function/method. For example, jQuery's $.ajax method [Documentation] describes an option called context:


    FAQ: Using object methods as callbacks/event handlers

    Another common manifestation of this problem is using object methods as callbacks/event handlers. In JavaScript, functions are first-class citizens, and the term "method" simply refers to a function that is the value of an object's property. But there is no specific link between the function and its "containing" object.

    Consider the following example:

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

    Function this.method is assigned as the click event handler, but if document.body is clicked, the recorded value will be undefined because in the event Inside the handler, this refers to document.body, not an instance of Foo.
    As mentioned before, this refers to how the function is called , not how is defined .
    It might be more obvious if the code looks like this:

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

    Solution Same as mentioned above: use .bind to explicitly bind this to a specific value

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

    Or by using an anonymous function as a callback/event

    reply
    0
  • Cancelreply