2016-04-25 2 views
3

В онлайн-курсе Кайл Симпсон говорит, что следующий код демонстрирует необходимость подъема в javascript, потому что без подъема «одна из функций всегда будет объявлена ​​слишком поздно».Поднимать действительно нужно в javascript, чтобы обеспечить взаимную рекурсию?

a(1) // 39 

function a(foo){ 
    if (foo > 20) return foo 
    return b(foo+2) 
} 

function b(foo){ 
    return c(foo) + 1 
} 

function c(foo){ 
    return a(foo*2) 
} 

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

var a = function(foo){ 
    if (foo > 20) return foo 
    return b(foo+2) 
} 

var b = function(foo){ 
    return c(foo) + 1 
} 

var c = function(foo){ 
    return a(foo*2) 
} 

a(1) // 39 

Так что это за история? Удобство и размещение вызова в сторону, есть ли ситуации, которые требуют Подъемник?

+0

второй будет выдавать ошибку, если 'a (1)' вызывается перед определениями функций. – gurvinder372

+0

Да, но это тривиально верно для любой функции и ее вызова (как я отмечаю в вопросе). Требование Симпсона, похоже, состоит в том, что для взаимной рекурсивной структуры функций требуется подъем, но это, похоже, не так. – rswerve

+0

В первом случае, если вы дадите 'a (1)' перед определением функции, это не даст такой же ошибки. – gurvinder372

ответ

1

Претензия, которую я сделал в отношении неосознанного JS, неспособного поддерживать взаимную рекурсию, является лишь догадкой для иллюстративных целей. Он разработан, чтобы помочь понять, что язык должен знать о переменных, доступных в области (областях). Это не рецепт для точного языкового поведения.

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

Более того, невозможно полностью проверить эту гипотезу только в JS. Фрагмент в OP использует только часть уравнения, а именно, что он использует выражения функций вместо деклараций функций, чтобы избежать подъема функции.

Язык, который я использовал для сравнения, представляет собой C, который, например, требует, чтобы сигнатуры функций объявлялись в .h заголовочных файлах, чтобы компилятор знал, как выглядит функция, даже если она не «увидела», это еще. Без него компилятор задыхается. Это своего рода ручное поднятие в некотором смысле. C делает это для проверки типов, но можно представить, что это требование существует по другим причинам, кроме этого.


Другой способ думать об этом, является ли JS является компилируемый язык, где было обнаружено все, прежде чем он выполняет, или это интерпретируется ли сверху вниз в один проход.

Если JS интерпретировался по принципу «сверху вниз», и он получил определение функции a(), которая ссылалась на b() внутри нее, что она еще не видела, это может быть проблемой. Если это выражение вызова было обработано без лени, в тот момент двигатель не мог понять, что будет с вызовом b(), потому что b() еще не обработан. Некоторые языки ленивы, а некоторые не ленивы.

Как и прежде, JS скомпилирован перед выполнением, поэтому двигатель открыл все функции (например, «подъем») перед запуском любого из них. JS также рассматривает выражения как ленивые, поэтому вместе это объясняет, почему взаимная рекурсия работает нормально.

Но если у JS нет подъема и/или не было ленивым, можно представить, что двигатель JS не сможет обрабатывать взаимную рекурсию, поскольку круговая ссылка между a() и b() на самом деле означает, что один из двух всегда был объявлен "слишком поздно".

Это действительно все, что я имел в виду в книге.

+0

Это очищает мой вопрос. Спасибо за ответ, и курс, который является фантастическим. – rswerve

+0

Не могли бы вы рассказать о «_JS также относится к выражениям как lazy_». Не оценивается ли JS? – rand

+0

Я имел в виду, что значение 'a' в' a() 'не разрешается до момента его запуска. –

-1

Второй блок кода работает нормально, потому что вы вызываете a(1) после того, как все функции инициализированы. Попробуйте следующий блок:

var a = function(foo){ 
 
    if (foo > 20) return foo 
 
    return b(foo+2) 
 
} 
 

 
var b = function(foo){ 
 
    return c(foo) + 1 
 
} 
 

 
a(1); 
 

 
var c = function(foo){ 
 
    return a(foo*2) 
 
}

Это даст ошибку Uncaught TypeError: c is not a function потому function assigned to c не поднят. Вот почему вам нужен подъем.

Потому что, если вы объявляете функции как в вашем первом блоке кода, все функции будут подняты, и вы можете вызывать a в любом месте кода. В других случаях это неверно.

4

Удобство и размещение вызова в стороне, нет никаких ситуаций, требующих подъема.

Просто убедитесь, что объявили все функции перед их использованием.

Примечание: В некотором браузере function a(){} создает функцию с именем a, а var a = function(){} не является (считается анонимной функцией). Имя функции используется при отладке. Вы также можете сделать var b = function a(){}.