setTimeout在Uncaught TypeError中返回错误:Chrome中的AudioContext中的非法调用

Hou*_*ter 4 javascript audiocontext

在Chrome中,我首先使用AudioContext创建连续音:

var audioCtx = new (window.AudioContext || window.webkitAudioContext);

var oscillator = audioCtx.createOscillator();
var gainNode = audioCtx.createGain();

oscillator.connect(gainNode);
gainNode.connect(audioCtx.destination);

oscillator.start();
Run Code Online (Sandbox Code Playgroud)

现在我想在几毫秒后停止它.所以我这样做:

setTimeout(oscillator.stop, 500)
Run Code Online (Sandbox Code Playgroud)

这将返回错误Uncaught TypeError: Illegal invocation.

但是,如果我这样做;

setTimeout(function(){oscillator.stop()}, 500)
Run Code Online (Sandbox Code Playgroud)

它工作正常.

我想现在为什么第一个不起作用并返回错误.这似乎是直截了当的方式.

rad*_*aph 7

您的原始代码不起作用,因为stop函数传递给setTimeout没有任何上下文 - 它不知道它应该作用于哪个对象.如果你像这样调用它:

oscillator.stop();
Run Code Online (Sandbox Code Playgroud)

然后在其中stop,将特殊变量this设置为指向的对象oscillator.但如果你像这样引用它:

var x = oscillator.stop;
Run Code Online (Sandbox Code Playgroud)

实际上没有调用该函数.相反,简单地从oscillator其他地方提取和存储对该函数的引用.该函数不记得它来自何处,并且可以同时存储在许多不同的变量或对象属性中.例如:

var x = {};
x.foo = oscillator.stop;
x.foo();
Run Code Online (Sandbox Code Playgroud)

最后一行调用stop上下文x(this设置为x)而不是oscillator.(函数的主体会导致错误,因为stop它假设它的上下文是什么样的,但是调用本身是合法的.)或者,如果你这样做:

var foo = oscillator.stop;
foo();
Run Code Online (Sandbox Code Playgroud)

然后stop将仅使用默认上下文调用.在严格模式下,this将设置为undefined,并在非严格模式下,this将设置为window.

当你这样做:

setTimeout(function(){oscillator.stop()}, 500)
Run Code Online (Sandbox Code Playgroud)

匿名函数stop使用适当的上下文调用.如果@elclanrs在评论中建议您执行此操作:

setTimeout(oscillator.stop.bind(oscillator), 500)
Run Code Online (Sandbox Code Playgroud)

它实际上是相同的:创建一个匿名函数,stop使用上下文调用oscillator.