Kayson
Kayson

Reputation: 405

Redefining a javascript class/object constructor to assign a global variable

I need to put a hook into Youtube's iFrame API that assigns the resulting player to a global variable (I can't touch the code that actually calls the API).

The API gets called like so:

player = new YT.Player('player', {
          height: '390',
          width: '640',
          videoId: 'M7lc1UVf-VE',
          events: {
            'onReady': onPlayerReady,
            'onStateChange': onPlayerStateChange
          }
        });

What I'd like to do is something like this:

YT.OldPlayer = YT.Player
YT.Player = function() {
    window.YTPlayer = new YT.OldPlayer(arguments)
    return window.YTPlayer
}
YT.Player.prototype = YT.OldPlayer.prototype

But for some reason calls to new YT.Player(), after I've modified it, cause an error that doesn't get called with the original. I know the new constructor is being called, but something in the call to YT.OldPlayer() causes the error, because window.YTPlayer is undefined. What am I doing wrong?

Upvotes: 0

Views: 257

Answers (3)

lucascaro
lucascaro

Reputation: 19347

This may not be the answer you are looking for, but as a PSA:

What am I doing wrong?

I think the main problem here is that you are attempting to monkey-patch functionality on an external library.

This is not a good idea not only because it may not work as you think it works, but also because it may break in the future without any warnings.

  • You are modifying functionality on a function that the library may use internally.
    There may be other functionality in the library that makes assumptions and depends on specific behaviors of this function, and you don't have much visibility unless you read the entire library.

  • Even if it works, the library may change its assumptions at any time, without warning.
    Have in mind that you will probably not test all the use cases for this function, and that the authors of the library make no commitment about how internal functionality works. They can change their code to use, or stop using, the function in question, change the internals of how the function works, etc, without really needing to advertise the changes. This will almost certainly break your code in unforeseeable ways, so you should stay away from this pattern as much as possible.

In conclusion: If you can, find a way of solving your problem without touching internals of libraries you don't control you will save yourself a lot of trouble.


If you still need to do this

If you still absolutely need to do this, your problem seems to be that you are passing arguments as an array-like object which is not what you get when your constructor is called.

If you can use ES6 rest parameters and spread syntax, you can do:

YT.OldPlayer = YT.Player
YT.Player = function(...args) {
    window.YTPlayer = new YT.OldPlayer(...args)
    return window.YTPlayer
}
YT.Player.prototype = YT.OldPlayer.prototype

If you can't use either, because you need to support IE and don't have any transpilation set-up, for example, you would need to find a workaround:

YT.OldPlayer = YT.Player
YT.Player = function() {
    window.YTPlayer = new (YT.OldPlayer.bind.apply(YT,arguments))
    return window.YTPlayer
}
YT.Player.prototype = YT.OldPlayer.prototype

Upvotes: 2

Kayson
Kayson

Reputation: 405

The solution I ended up with is

YT.OldPlayer = YT.Player
YT.Player = function() {
    window.YTPlayer = new YT.OldPlayer(...arguments)
    return window.YTPlayer
}
YT.Player.prototype = YT.OldPlayer.prototype

Upvotes: 0

customcommander
customcommander

Reputation: 18931

Try applying the arguments: window.YTPlayer = new YT.OldPlayer.apply(this, arguments)

Upvotes: 0

Related Questions