Определение [[Call]]
для связанных объектов функций опускает PrepareForTailCall()
.Означает ли это, что связанные функции не поддерживают надлежащие хвостовые вызовы, и что связанная функция, вызывающая себя рекурсивно, может взорвать стек?
Нет.PrepareForTailCall
происходит в EvaluateDirectCall
во время вычисления выражения вызова , где он проверяет, находится ли это выражение в хвостовой позиции.Когда хвостовой вызов подготовлен, текущий контекст выполнения сбрасывается до того, как функция будет вызываться , отправляя соответствующий внутренний метод [[Call]]
.Новый контекст текущего выполнения устанавливается в PrepareForOrdinaryCall
из метода [[Call]]
пользовательских функций .Метод связанных функций [[Call]]
просто вводит дополнительный уровень косвенности до того, как это произойдет.
В Спецификации языка ECMAScript 2015 определения Function.prototype.apply
и Function.prototype.call
оба включают " Выполнить PrepareForTailCall()
" в качестве одного из своих шагов, поэтому мы знаем, что эти функции поддерживают правильные вызовы хвоста.
Да, это необходимо, потому что [[Call]]
метод встроенных функций устанавливает новый контекст выполнения (для «шагов, определенных реализацией»).Именно этот «встроенный контекст» будет отброшен PrepareForTailCall
до вызова фактической функции.
Методы call
и apply
являются функциями, вызывающими функции, и при их вызове есть два вызовав стеке, который должен быть оптимизирован с помощью хвостового вызова.(Сравните это, например, с Array.prototype.map
, который также вызывает другие функции, но здесь контекст выполнения map
остается в стеке вызовов. В call
и apply
, Call()
действительно находится в хвостеположение алгоритма).