我在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错误设计的丑陋黑客。
所以这是我的问题:如何以优雅的方式实现这一目标?
答案 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
,就像你所做的一样。