user3292788
user3292788

Reputation: 417

Bind this in nested literal object

Let's say I have this code

(function() {

    function Foo(arg) {
        this.name = arg;
    }

    Foo.prototype = {
        bar: {
            baz: function() {
                alert(this.name); // Undefined...
            }
        }
    }

    var foo = function(arg) {
        return new Foo(arg);
    };


    window.foo = foo;

    return foo;
}());

foo("Anything").bar.baz();

How can I make "this" in my function "baz" refers to the object Foo without using bind or apply when I call it from outside ?

Upvotes: 1

Views: 85

Answers (2)

T.J. Crowder
T.J. Crowder

Reputation: 1074168

FWIW, I would strongly recommend not building nested structures like that, or at least not on the prototype, because the bar object is shared amongst all of the instances, which opens the door to a lot of cross-talk-style bugs. Instead, I'd create bar within the constructor.

How can I make "this" in my function "baz" refers to the object Foo without using bind or apply when I call it from outside ?

You may have bind and apply/call slightly confused. You wouldn't use bind when calling the function, but when creating it. Unless you use bind (or something equivalent to it), you can't do what you've said you want, because absent bind (or similar), this is set by how the function is called, and so this.bar.baz() will make this be this.bar within the call.

Here's how you'd build bar within the constructor, and use bind to make baz use the correct this:

function Foo(arg) {
    this.name = arg;
    this.bar = {
        baz: function() {
            alert(this.name);
        }.bind(this)            // <== Note
    };
}

Example:

function Foo(arg) {
  this.name = arg;
  this.bar = {
    baz: function() {
      snippet.log(this.name);
    }.bind(this)            // <== Note
  };
}

var f1 = new Foo("f1");
var f2 = new Foo("f2");
f1.bar.baz(); // "f1"
f2.bar.baz(); // "f2"
<!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>


More about cross-talk: The naive thing to do is to just add one line to your Foo constructor, and keep bar on the prototype:

this.bar.baz = this.bar.baz.bind(this);

That would be a very bad idea, because you'd get cross-talk between instances:

function Foo(arg) {
  this.name = arg;
  this.bar.baz = this.bar.baz.bind(this); // DON'T DO THIS
}

Foo.prototype = {
  bar: {
    baz: function() {
      snippet.log(this.name);
    }
  }
};

var f1 = new Foo("f1");
var f2 = new Foo("f2");
f2.bar.baz(); // "f1" -- cross talk! Should be f2
<!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>

Upvotes: 2

Jeff Panici
Jeff Panici

Reputation: 142

Use bind at declaration time to properly scope this, e.g.

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

Upvotes: 0

Related Questions