Search code examples
javascriptdom-eventsprototype

Why does not bind() or apply() work here, but call() does?


Take this very simple framework I am experimenting with (so that I can learn JavaScript's function prototype more in depth.)

(function(){
var app = {
    ui: {
        app: document.querySelector('.app'),
        settings: document.querySelector('.settings'),
    },
    actions: 'click settings openSidebar'
    ,
    functions: {
        openSidebar: function(e){
            console.log(this); // <- expected value of this is 'app.ui'
        }
    },
    run: function(){
        var a1 = this.actions.split("\n");
        var a2 = this.actions.split(" ");

        var self = this;
        this.ui[a2[1]].addEventListener(a2[0], function(e){
            app.functions.openSidebar.call(self.ui,e);
        });
    }
};
app.run();
})();

This works great. Output from console is:

Object {ui: Object, actions: "click settings openSidebar", functions: Object, run: function}

However, when I try to do it like this:

var self = this;
this.ui[a2[1]].addEventListener(a2[0], function(e){
    app.functions.openSidebar(e);
}.bind(this));

The context of this in openSidebar() is openSidebar (aka bind has no effect). Console output:

Object {openSidebar: function}

However, when I try to use the apply function, like so:

app.functions.openSidebar.apply(self.ui,e);

It works fine (this in openSidebar is app.ui) EXCEPT that the argument (e) does not get passed, so e == undefined.

Here goes:

1. Why does not bind work (at all) in the first example?

2. Why does apply work without passing arguments (the e (event))?

And for added brownie points:

3. Why does call work as expected?


Solution

  • Why does not bind work (at all) in the first example?

    It "works", it just doesn't do what you expect.

    Inside your anon function this is indeed the value set by bind. However, when you then call a function that is also a property of an object (functions.openSidebar) then for that invocation this is automatically bound to that object inside the function (i.e. this === functions). The value of this from the parent context is never "inherited" down the call chain.

    Why does apply work without passing arguments (the e (event))?

    Because apply tries to pull out the arguments for the call from its own second argument e by treating it as an array. This means that its length property is checked first; your event object doesn't have a length so apply gets the undefined value produced and effectively behaves as if you had passed in an empty array. See the annotated ES5 reference for the details.

    Why does call work as expected?

    This is a strange question. Why would it not work? You are using it exactly like it's meant to be used.