shrewdbeans
shrewdbeans

Reputation: 12539

How do the following Javascript functions affect the value of 'this'?

I have an object which has two functions within it, and as I guessed each one has a different value for this:

custom_controls : {
    play_pause : function () {

        console.log(this); // object

        videoPlayer.getIsPlaying(function (video_is_playing) {
            if (video_is_playing) {

                console.log(this); // window

                videoPlayer.pause(true);
            } else {
                videoPlayer.play();
            }
        });
    }
},

Then the function is invoked like this:

custom_controls.play_pause()

I've heard that the way you invoke a function denotes the value of this.

So my question is: What is happening here? What kind of function invocations am I using? And how does each one affect this?

Upvotes: 0

Views: 117

Answers (6)

kunev
kunev

Reputation: 63

Each function is actually executed within a context. That context is denoted as the current this for which you call the function.

Given your code: If you call custom_controls.play_pause() you are saying "Take the field of the object custom_controls named play_pause and execute it within the context of the object custom_controls".

Later on calling videoPlayer.getIsPlaying() means pretty much the same. Except you're giving it a callback function. How that callback function is executed later on depends on how videoPlayer.getIsPlaying is implemented. If I have to guess I'd say that getIsPlaying has a callback.call(window, video_is_playing) somewhere in it.

call is a method of all function objects in javascript.

There are a few ways to work around this "issue" if you want to reference a this in some callback.

var self = this;
call_me_maybe(function() {
    console.log(this); //the this that call_me_maybe chose to call your function with
    console.log(self); //the this from the upper scope
});

or if you don't care about the object in which context call_me_maybe will call your function:

call_me_maybe((function(){
    console.log(this); //the this from the upper scope
}).bind(this));

What bind does is it returns a wrap[per of the function which will always be called in the context of the object to which it is bound. bind can also bind arguments as well as the this object for the function, creating a sort of curry.

Upvotes: 1

Ray
Ray

Reputation: 243

Your first this is referring to play_pause.

Your second this could either be referring to the Window or your videoPlayer object. In JavaScript, closures and regular functions are 'generally' attached to window, and calling this returns window. In certain cases, e.g. if you attach a function to the click handler of an HTML element, this refers to the element...

element.onclick = function(){
  this // -> element
}

But generally if you just create a function(), or have an anonymous one like yours this refers to window.

function hello(){
  this // -> window
}

Upvotes: 1

Tibos
Tibos

Reputation: 27823

When calling obj.func(), this inside the function will be equal to obj. If there is no obj, the global object (window) is used instead. Or if you are running in Strict Mode, undefined is used.

The first log is your object because you call the function like this:

custom_controls.play_pause() // custom_controls will be 'this'

The second log is window because the function passed as parameter to getIsPlaying is not called with any this:

videoPlayer.getIsPlaying = function(callback) {
  callback(); // this inside callback will be window
}

You can control what the value of this will be when you invoke a function by using call or apply. You can create a new function which will always have the this value set to whatever you want by using the bind function:

videoPlayer.getIsPlaying(function (video_is_playing) {
        if (video_is_playing) {

            console.log(this); // my obj

            videoPlayer.pause(true);
        } else {
            videoPlayer.play();
        }
    }.bind(this)); // magic!

Reference: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind

Upvotes: 2

closure
closure

Reputation: 7452

When you call videoPlayer.getIsPlaying it accepts a callback fn. Callback is invoked directly like cb() and hence the context is global (window).

To achieve the callback happening in context of your object. You can use

var fn = cb.bind(customControl);
videoPlayer.getIsPlaying(fn);    

As a rule of thumb when a function is called like object.function this is set to object. If function is called directly, this is set to window. Function.bind return a function after binding object (this value) optionally along with parameters.

Read: MDN https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind

Upvotes: 0

Asenar
Asenar

Reputation: 7010

this refers to the "proprietary" of the function, or the object from the function is a method of.

When you define a basic function, the "proprietary" is the page (or the window) itself.

You can check the callback documentation for workarounds

Upvotes: 0

krillgar
krillgar

Reputation: 12805

The this that you've discovered is the object is what you would expect because the function is operating on that object.

I'm not familiar with your videoPlayer, but as the value of this is the "window", I would imagine that either A the video player is a function of the browser itself, or B its scope was not properly closed.

Upvotes: 0

Related Questions