setTimeout возвращает ошибку с Uncaught TypeError: незаконный вызов в AudioContext в Chrome

В 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();

Теперь я хочу остановить это через несколько миллисекунд. Итак, я делаю это:

setTimeout(oscillator.stop, 500)

Это возвращает ошибку Uncaught TypeError: Illegal invocation,

Однако, если я делаю;

setTimeout(function(){oscillator.stop()}, 500)

это работает отлично.

Я хотел бы сейчас, почему первый не работает и возвращает ошибку. Кажется, это простой способ сделать это.

1 ответ

Решение

Ваш оригинальный код не работает, потому что stop функция передается setTimeout без какого-либо контекста - он не знает, на какой объект он должен воздействовать. Если вы вызываете это так:

oscillator.stop();

Затем в течение stopспециальная переменная this устанавливается на объект, на который указывает oscillator, Но если вы ссылаетесь на это так:

var x = oscillator.stop;

Функция на самом деле не вызывается. Скорее, ссылка на функцию просто извлекается из oscillator и хранится в другом месте. Функция не помнит, откуда она взялась, и может быть сохранена во множестве различных переменных или свойств объекта одновременно. Например:

var x = {};
x.foo = oscillator.stop;
x.foo();

Последняя строка звонков stop с контекстом x (this установлен в x) скорее, чем oscillator, (Тело функции вызовет ошибки, так как stop делает предположения о том, как выглядит его контекст, но сам вызов допустим.) В качестве альтернативы, если вы сделаете это:

var foo = oscillator.stop;
foo();

затем stop будет вызываться только с контекстом по умолчанию. В строгом режиме, this будет установлен в undefinedи в нестрогом режиме, this будет установлен в window,

Когда вы делаете это:

setTimeout(function(){oscillator.stop()}, 500)

Анонимная функция вызывает stop в правильном контексте. Если, как предложено @elclanrs в комментариях, вы делаете это:

setTimeout(oscillator.stop.bind(oscillator), 500)

Это фактически то же самое: создается анонимная функция, которая вызывает stop с контекстом oscillator,

Другие вопросы по тегам