2

In this example why does pushing the function reference onto the array not change the scope execution context of this whereas assigning the function reference to a new variable does change the scope execution context of this?

(function() {
    function Car(year) {
        this.year = year;
    }

    Car.prototype = {
        logYear: function() {
            console.log(this.year);
        }
    };

    var ford = new Car('Ford', 1999);

    ford.logYear();

    var cbs = [];

    cbs.push(ford.logYear);

    for(var x = 0; x < cbs.length; x++) {
        cbs[x](); // -> 1999 #1
        var callback = cbs[x];
        callback(); // -> undefined #2
    }
})()

Is this because the reference to the function in the array points to the original function on the ford object where this is still defined as the instance (#1) and the variable assignment changes the this to point to the wrapping IIFE block (#2)?

Is this a good pattern if I need to collect of bunch of methods like this from various objects and call them somewhere else?

How does this referential calling relate to examples like console.error.bind(console)?

Thanks!

-- Update

Thanks to @Satyajeet for the nice explanation and clarification.

Typos & copying errors aside the reason I was asking this question in the first place is that I suspected what Satyajeet confirmed but an application I am working on does not reflect this behavior (or so I thought).

It turns out that in the application I am performing a similar process to the above code where I call cbs.push(ford.logYear);, adding multiple methods from different objects to an array.

At a point in the app I call all these methods and expect them to behave in the same way as when they are called in the execution context scope of the original object... and they do, because the values the methods interact with are not attached to this, they are captured in closures.

See example plunker here

-- Update 2

Fixed usage of execution context and scope to be accurate for each part of the question/answer.

4
  • 1
    ` cbs[x](); // -> 1999 #1` will not print anything Ford or 1999, it is printing undefined Commented Feb 15, 2016 at 6:02
  • JS is blocked scoped. So this will only change when called in a function. Your cbs array is in the same scope as Car Commented Feb 15, 2016 at 6:04
  • 1
    Actually your first console log is from you doing ford.logYear(); you get undefined on all other log messages. So you are changing the scope of this, you are just misinterpreting the log messages Commented Feb 15, 2016 at 6:04
  • @PatrickEvans Ah, thanks! That's what I get for coding late and copypasta. Commented Feb 15, 2016 at 14:18

1 Answer 1

4

Ignoring your late night copy paste mistake, and redefining your Car function to

function Car(name, year) {
    this.name = name;
    this.year = year
}

#1 indeed more of a question about how Array works rather then about the context and scope(*#2)

You are calling a function inside an array, So in javascript each element of an array is a property of that Array Object. for example

var a = [];
a.push('Some value'); // a.0 is a property 

but you just can not call a.0 because in javascript properties that begin with a digit cannot be referenced with dot notation, and must be accessed using bracket notation

So when you are calling cbx[0]() it is essentially same as cbx.0(). And here comes the role of Scope and Execution context in javascript. There are already many good articles and answers about Scope and Context in javascript. Just like this, So i think its not worth to explain all that here.

But fundamentally this is not defined till the execution of function(this depends on Execution context rather then scope).

So your #1 will print undefined because cbs[0]() is equivalent to cbs.0(), And this(Execution context) is set to Array itself, which is [function]. (You only have a function inside it).

And your #2 will also print undefined because Execution context is global(window object in case of browser) there. Answer to your 3rd question is, You need to Explicitly hard bind Execution context to your function.

cbs.push(ford.logYear.bind(ford));

And your 4th question is i think just an another use case of ES5's bind method, Nothing special. It is normally used because browser implementations requires that execution context(this) of console.error must be set to window.console.

Sign up to request clarification or add additional context in comments.

3 Comments

Thanks for clarifying. I have a better understanding of bind now. It turns out the pattern I am using in the app I'm working on uses closures to maintain context when passing methods around, which is why adding methods to an array still works in my app's implementation. See my update to my question.
Ohh.. seems like you were having problem with Scope and i explained Context.. :)
I updated my question to fix my use of execution context and scope to be consistent and accurate when referring to closures vs this.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.