Dojo范围问题与“内部”需要

时间:2012-06-18 18:32:14

标签: javascript dojo

我确信“内在要求”不是正确的术语,但很快就会有意义。我正在从工具包的网站上学习Dijit教程,我遇到了一个问题,我认为这个问题比Dojo更容易理解Javascript。

我的页面初始化脚本:

require(['player', 'dojo/dom', 'ready'], function(Player, dom){
    var p = new Player({
        type : 'video',
        dimensions : [720, 480]
    });
    p.setSource('videos/myvideo.webm')

    p.placeAt(dom.byId('stage') )
})

我的dijit“player.js”的构造函数

constructor : function(opts){
    (function($){
        require(['sg/player/component/Video', 'sg/player/component/Audio'], function(Video, Audio){
            $._setMedia( (opts.type == 'video') ? new Video() : new Audio());
            console.log($._media) // outputs an object "a"
        })
    })(this);
    console.log(this._media) // also outputs the correct object, "a"
}

// the internal setter function used above
_setMedia : function(m){
    this._media = m;
},

(我有一个匿名函数的原因是我不喜欢在var self = this块中分配requirethis不是包含对象。)

当我在init脚本中创建new Player()对象时,我可以看到正确分配了AudioVideo的新实例。但是,当我在init脚本中调用p.setSource()时,出现_media为空的错误!

步骤

  • 创建播放器
    • 解析配置选项(类型,尺寸等)。内部_media对象包含VideoAudio
    • 的实例
  • 允许在Player来源之外访问setter方法!错误!!

所以我的问题,希望我提供了足够的背景,但为什么_media变量会失去它的价值?要从Dijit源外部访问的Player实例中的任何方法都不应该对内部变量的范围产生任何影响,但这似乎正在发生。构造函数返回后,应设置_media!但是使用

setSource : function(s){
    console.log('Setting source: ', s, this._media)
    // outputs ("Setting source: path/to/video.webm', null)
},

...抛出一个错误,因为它在_media内引用的setSource变量被认为是空的。

希望很清楚:)

更新

希望我能给你们两个勾选标记!感谢您抽出宝贵时间提供帮助。

@Frode:肯定有一些异步问题迫使我学习并尝试更多结构,所有这些都失败了,导致了这次更新。我认为在某些时候文件被缓存,导致变量内容不一致。

@phusick:我将您的建议与下面发布的内容混合在一起。

我考虑重做玩家将如何实例化的结构,对象arg等,但决定做以下事情,万一有人遇到这个问题......

我将AudioVideo类合并到_Media文件中,使用此结构(为简洁起见,删除了代码)

define(['dojo/_base/declare'], function(declare){
    var _base = declare("_Media", null, {        
        constructor : function(type){
            this._type = type;
        },
        // etc
    })
    return {
        Video : function(){
            return declare("Video", _base, {
                constructor : function(){
                    this.inherited(arguments, ['video'])
                }
                // etc
            })()
        },
        Audio : function(){
            return declare("Audio", _base, {
                constructor : function(){
                    this.inherited(arguments, ['audio'])
                }
                // etc
            })()
        },
    }
})

...这样一来,最初只加载了一个文件,它包含两个子类。当不使用一个文件时,比装载2个单独的文件更好,IMO。

对于玩家类型实例,它变为:

this._media = opts.type && opts.type == 'video' ? new Media.Video() : new Media.Audio();

到目前为止一切顺利!再次感谢。

2 个答案:

答案 0 :(得分:1)

  

构造函数返回后,应设置_media!

我认为这是你采取错误措施的地方。请记住,require是一个异步函数!

但是,我对你的构造函数中的这一行(特别是注释)感到有点困惑:

console.log(this._media) // also outputs the correct object, "a"

你是100%确定这输出“a”而不是null吗?如果你是,请忽略我的其余答案,因为那时我误解了一些事情:)

好的,如果您还在阅读,我会尝试解释异步需求。当你打电话:

require([".../Video",".../Audio"], function(Video, Audio) {
    // do something and set _media
});

你基本上是在说:“浏览器,你在后台为我提取视频和音频模块,而我继续我的下一行代码。当你提取它们时,运行我给你的功能集_media“。

换句话说,在require()完成并设置_media之前可能需要很长时间,但您的代码会立即继续。所以当你调用setSource时,require()可能还没有完成(事实上,它甚至可能没有开始下载任何东西)。

希望有所帮助!

答案 1 :(得分:1)

@Frode是对的。实际上,即使异步操作足够快也无法工作,因为JavaScript是单线程的,异步回调进入事件队列并通过Event Loop一个接一个地执行。

请参阅我对Dojo 1.7 how to use dojo components outside of require()的回答。

你的 player.js 是否有任何理由不这样:

define([
    "sg/player/component/Video",
    "sg/player/component/Audio"
], function(
    Video,
    Audio
) {

    return declare([SomeSuperClass], {

        constructor: function(opts) {
            var media = opts.type && opts.type == "video" ? new Video() : new Audio();
            this._setMedia(media);
        }
    });

});