使用Javascript

时间:2017-10-25 08:29:42

标签: javascript callback

我在Javascript中遇到了回调问题。我使用我称之为Javascript的丑陋属性来解决我的问题(所以说,从某种程度上说,这将是禁止的,并且永远不会在除Javascript之外的其他语言中工作)。所以我的问题是:是否有一种优雅的方式,做同样的事情。

我将从头开始。我的目标是以某种方式包装Web Audio API。在架构中,我实现了一个类,让我们调用它AudioRessource,它注定是Web Audio API的AudioBuffer对象的某种方式的接口(抽象)。

这个类(AudioRessource)有一个原型成员函数,它必须简单地将一个url作为参数来自动加载音频数据,解码它,处理错误等,最后保存生成的AudioBuffer对象a"伪私人"构件:

function AudioRessource() 
{
   this._aBuffer = null; // future reference to `AudioBuffer` object
   this._loadStatus = 2;
};

AudioRessource.prototype.loadData = function(url) {
  /* deal here with async functions to 
     provides audio data loading automation */
}

这里的主要问题是,这将是一个对象实例(AudioRessource),它将仅使用本地引用创建回调函数,并且必须能够传递最终的AudioBuffer对象对自己。

要加载原始音频数据,这非常简单,我使用XMLHttpRequest对象,并将额外的属性设置为XMLHttpRequest对象的成员,如下所示:

AudioRessource.prototype.loadData = function(url) {
    let req = new XMLHttpRequest();
    req.extraProperty = this; // reference to `AudioRessource` instance
    req.onload = function(){
       // retrive instance reference within the callback
       this.extraProperty._loadStatus = 0;
    }
    req.onerror = function(){ 
       // retrive instance reference within the callback
       this.extraProperty._loadStatus = -1;
    }
    req.open('GET', url, true);
    req.send(null);

    this._loadStatus = 1;
}

当我们必须将编码的原始音频数据解码为PCM数据(即Web Audio API AudioBuffer对象实例)时,会出现一个大问题。实际上,Web Audio API只提供了一个函数来实现这一点,而且这个函数是异步的,它接受一个简单地将结果缓冲区作为参数接收的回调:如何" catch"这个结果缓冲区将它分配给正确的AudioRessource实例(推动进程的那个)?这样的工作方式:

AudioCtx.decodeAudioData(rawData, 
                        function(result){ 
                          // do something with result },
                        function(error){
                          // do something with error });

我的第一个天真的方法就是像我们在C / C ++中一样思考:我只是简单地设置一个AudioRessource实例函数"指针" (引用)作为回调,这样,AudioRessource实例将直接接收缓冲区:

// where 'this' is an `AudioRessource` instance
AudioCtx.decodeAudioData(rawData, 
                        this._handleDecodeSuccess,
                        this._handleDecodeError);

然而,这不起作用,因为在javascript中,这不是"函数指针"传递到decodeAudioData,但是如果我还不符合,则是一个文字表达式,即" ASCII内容"功能...所以'这个'参考丢失了!

我花了一些时间来尝试理解这种异步函数是如何工作的,因为对我来说,来自C / C ++,这只是一个异端:函数不需要任何额外的参数,无法通过任何外部参考......"那是什么东西?"。然后我最终决定尝试" Illogical Javascript逻辑"方式......我找到了解决方案:

 // Create local variable which stores reference to 'this'
 let thisInstReference = this; 

 // Use the local variable to write our callback
 AudioCtx.decodeAudioData(rawData, 
                        function(resut){
                          thisInstReference._aBuffer = result;
                          thisInstReference._loadStatus = 0;
                        },
                        function(resut){
                          thisInstReference._loadStatus = -3;
                        });

对我而言,这真是太可怕了。首先,我甚至不理解真正发生的事情:如何使用存储对象实例(this)的引用的局部变量(对象实例'成员函数)&# 34;因为这"在回调函数中?我甚至不明白语言是如何允许这种事情的。其次,对我而言,这不是一种正确的方式"编写代码:这段代码简直不合逻辑,很脏,这很有效,但这看起来像是一个利用Javascript错误设计的丑陋黑客。

所以这是我的问题:如何以优雅的方式实现这一目标?

1 个答案:

答案 0 :(得分:0)

您的问题仅仅归因于this在javascript中的工作方式。 this的值不是在编译时绑定,也不是在运行时绑定,而是在调用时很晚。

在以下代码中:

AudioCtx.decodeAudioData(rawData, 
            this._handleDecodeSuccess,
            this._handleDecodeError);

.. this_handleDecodeSuccess内的_handleDecodeError的值不是在对象创建时确定的,而是在它们被调用时确定的。而且,decodeAudioData方法最终会在解码完成时调用它们。这会导致this的值变为其他值(取决于函数的调用方式)。

现代解决方案是将this静态绑定到函数:

AudioCtx.decodeAudioData(rawData, 
            this._handleDecodeSuccess.bind(this),
            this._handleDecodeError.bind(this));
  

注意:.bind()方法创建一个新函数,它将函数与this包装,永久绑定到传递给它的参数。

传统的解决方案是在闭包内捕获this,就像你所做的一样。