在javascript中安全地定义公共回调函数的变量

时间:2012-06-11 03:05:17

标签: javascript callback this google-closure-library youtube-javascript-api

我正在使用YouTube iFrame API在网页上嵌入大量视频。这里的文档:https://developers.google.com/youtube/iframe_api_reference#Requirements

总之,您使用以下代码段异步加载API:

 var tag = document.createElement('script');
 tag.src = "http://www.youtube.com/player_api";
 var firstScriptTag = document.getElementsByTagName('script')[0];
 firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);

加载后,API会触发预定义的回调函数onYouTubePlayerAPIReady

有关其他上下文:我在Google Closure中为此定义了一个库文件。我提供了一个命名空间:goog.provide('yt.video');

然后我使用goog.exportSymbol以便API可以找到该功能。一切正常。

我的挑战是我想将2个变量传递给回调函数。如果没有在window对象的上下文中定义这两个变量,有没有办法做到这一点?

goog.provide('yt.video');

goog.require('goog.dom');

yt.video = function(videos, locales) {
  this.videos = videos;
  this.captionLocales = locales;

  this.init();
};

yt.video.prototype.init = function() {
  var tag = document.createElement('script');
  tag.src = "http://www.youtube.com/player_api";
  var firstScriptTag = document.getElementsByTagName('script')[0];
  firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
};

/*
 * Callback function fired when YT API is ready
 * This is exported using goog.exportSymbol in another file and
 * is being fired by the API properly.
 */
 yt.video.prototype.onPlayerReady = function(videos, locales) {
    window.console.log('this :' + this); //logs window
    window.console.log('this.videos : ' + this.videos); //logs undefined
    /*
     * Video settings from Django variable
     */
    for(i=0; i<this.videos.length; i++) {
      var playerEvents = {};
      var embedVars = {};

      var el = this.videos[i].el;
      var playerVid = this.videos[i].vid;
      var playerWidth = this.videos[i].width;
      var playerHeight = this.videos[i].height;
      var captionLocales = this.videos[i].locales;
      if(this.videos[i].playerVars)
        var embedVars = this.videos[i].playerVars;
      }
      if(this.videos[i].events) {
        var playerEvents = this.videos[i].events;
      }

      /*
       * Show captions by default
       */
      if(goog.array.indexOf(captionLocales, 'es') >= 0) {
        embedVars.cc_load_policy = 1;
      };

      new YT.Player(el, {
        height: playerHeight,
        width: playerWidth,
        videoId: playerVid,
        events: playerEvents,
        playerVars: embedVars
    });
 };

};

为了初衷这个,我目前在一个自动执行的匿名函数中使用以下内容:

  var videos = [
    {"vid": "video_id", "el": "player-1", "width": 640, "height": 390, "locales": ["es", "fr"], "events": {"onStateChange": stateChanged}}, 
    {"vid": "video_id", "el": "player-2", "locales": ["es", "fr"], "width": 640, "height": 390}
  ];

  var locales = ['es'];

  var videoTemplate = new yt.video(videos, locales);

2 个答案:

答案 0 :(得分:2)

如何将onYouTubePlayerAPIReady定义为全局函数,如API期望的那样,然后从该函数内部调用onPlayerReady方法?代码示例:

window.onYouTubePlayerAPIReady = function () {
    var args = Array.prototype.slice.call(arguments);
    args.push(videos, locales);
    videoTemplate.onPlayerReady.apply(videoTemplate, args);
};

并修改onPlayerReady方法的签名以接受相同顺序的参数

答案 1 :(得分:1)

回答您的具体问题:

  

我的挑战是我想将2个变量传递给回调函数。如果没有在窗口对象的上下文中定义这两个变量,有没有办法做到这一点?

是的,有一些方法可以在不使用全局变量的情况下向回调函数提供数据。但是,在查看回调函数之前,YouTube API具有以下要求:

  

In addition, any HTML page that contains the YouTube player must implement a JavaScript function named onYouTubePlayerReady. The API will call this function when the player is fully loaded and the API is ready to receive calls.

YouTube API examples onYouTubePlayerReady 功能中添加事件监听器,如下所示:

function onYouTubePlayerAPIReady() {
  var player;
  player = new YT.Player('player', {
    width: 1280,
    height: 720,
    videoId: 'u1zgFlCw8Aw',
    events: {
      'onReady': onPlayerReady,
      'onPlaybackQualityChange': onPlayerPlaybackQualityChange,
      'onStateChange': onPlayerStateChange,
      'onError': onPlayerError
    }
  });
} 

您的示例回调函数yt.video.prototype.onPlayerReady似乎是YouTube onReady对象发出的YouTube API事件YT.Player的事件监听器。 API文档表明在构造YT.Player对象时添加了事件侦听器,或者使用addEventListener函数添加了事件侦听器(Closure Library替代为goog.events.listen)。

由于yt.video.prototype.onPlayerReady事件监听器需要“监听”从YouTube onReady对象发出的YT.Player事件,因此在其中构建新的YT.Player个实例将是循环的事件侦听器yt.video.prototype.onPlayerReady旨在侦听YT.Player实例发出的事件。

回到在不使用全局变量的情况下向回调函数提供数据的原始问题,您不能将任意函数参数传递给事件侦听器(例如,在上面的原始代码示例中,yt.video.prototype.onPlayerReady必须接受{ {1}}对象作为其第一个参数而不是Event数组)。但是,事件侦听器videos可以用作回调函数,并且仍然可以访问yt.video.prototype.onPlayerReady实例对象的状态,只要它绑定到{{1}的实例即可}。

yt.video参数绑定到特定对象的一种方法是使用Closure Library函数goog.bind(functionToCall, selfObject, var_args)。可以按如下方式修改yt.video构造函数:

this


然后可以将绑定事件侦听器添加到yt.video对象,如下所示:

goog.provide('yt.video');

goog.require('goog.dom');

/**
 * @constructor
 */
yt.video = function(videos, locales) {
  this.videos = videos;
  this.captionLocales = locales;

  this.onPlayerReadyListener = goog.bind(this.onPlayerReady, this);

  this.init();
};

yt.video.prototype.init = function() {
  var tag = document.createElement('script');
  tag.src = "http://www.youtube.com/player_api";
  var firstScriptTag = document.getElementsByTagName('script')[0];
  firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
};

/*
 * Callback function fired when YT API is ready
 * This is exported using goog.exportSymbol in another file and
 * is being fired by the API properly.
 */
yt.video.prototype.onPlayerReady = function(event) {
  // Logs [object Object]
  window.console.log('this :' + this);

  // Logs [object Object],[object Object]
  window.console.log('this.videos : ' + this.videos); 

  event.target.playVideo();
};