Поддерживают ли связанные функции правильные хвостовые вызовы в ES6?

В спецификации языка ECMAScript 2015, определения Function.prototype.apply а также Function.prototype.call оба включают "Perform PrepareForTailCall()" в качестве одного из своих шагов, поэтому мы знаем, что эти функции поддерживают надлежащие вызовы хвоста (т. е. оптимизация хвостового вызова).

Однако определение [[Call]] для объектов связанных функций опускает PrepareForTailCall(). Означает ли это, что связанные функции не поддерживают надлежащие хвостовые вызовы и что связанная функция, вызывающая себя рекурсивно, может взорвать стек?

2 ответа

Решение

Определение [[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() действительно в хвостовой позиции алгоритма).

См. §12.3.4.1 ( Семантика времени выполнения: оценка). Во-первых, мы делаем IsInTailPositionтогда мы делаем EvaluateDirectCall - результат которого F.[[Call]], Другими словами, к тому времени, когда мы делаем реальный вызов, мы уже знаем, находимся ли мы в хвостовой позиции.

Однако, как вы упомянули, JS не оптимизирован хвостом ни в одном браузере, кроме Safari.

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