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
,