Информация о процессе выполнения запущенной функции хранится в её контексте выполнения (execution context).
Контекст выполнения – специальная внутренняя структура данных, которая содержит информацию о вызове функции. Она включает в себя конкретное место в коде, на котором находится интерпретатор, локальные переменные функции и прочую служебную информацию.
Один вызов функции имеет ровно один контекст выполнения, связанный с ним.
Когда функция производит вложенный вызов, происходит следующее:
- Выполнение текущей функции приостанавливается.
- Контекст выполнения, связанный с ней, запоминается в специальной структуре данных – стеке контекстов выполнения.
- Выполняются вложенные вызовы, для каждого из которых создаётся свой контекст выполнения.
- После их завершения старый контекст достаётся из стека, и выполнение внешней функции возобновляется с того места, где она была остановлена.
https://learn.javascript.ru/recursion#kontekst-vyp...
Функция перебирает ключи объекта и на моменте createTreeText(obj[key]) интерпретатор берет значение ключа, останавливается, создает новый контекст выполнения, идет туда и заново идет с начала функции, обрабатывая значение ключа. Рано или поздно в функцию в качестве аргумента попадает пустой объект. У пустого объекта нет ключей, нечего перебирать, цикл не запускается, некому вызвать рекурсию, поэтому интерпретатор может пройти дальше по телу функции, обработать if, return, и по окончанию вернуться в предыдущий контекст выполнения на то место, где остановился, а именно
'<li>' + key + сюда + '</li>'
. Там переходит к следующему ключу, опять уходит в рекурсию, потом опять рано или поздно возвращается.