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

Фрагмент 1:

var a; // undefined variable named 'a'
function a(foo) { // a function named 'a'
  var foo = "Hello World";
  console.log(foo);
}
console.log(a); // output is: [Function: a], but why not undefined?

Фрагмент 2:

function a(foo) { // a function named 'a'
    var foo = "Hello World";
    console.log(foo);
}
var a; // undefined variable named 'a'
console.log(a); // output is: [Function: a], but why not undefined?

Я мог бы просто показать фрагмент 1, чтобы задать этот вопрос, однако я показал оба только для полноты картины. В них я также написал несколько кратких комментариев.

Мой вопрос: в обоих случаях, почему объявление функции «перезаписывает»/«затеняет» или имеет «приоритет» над объявлением переменной? Может кто-нибудь объяснить это поведение?

Я понимаю, что они, возможно, одинаковы (с точки зрения интерпретатора) с точки зрения конечного результата из-за явления «подъема», но почему объявление функции или объявление переменной имеет < strong>приоритет над другим, когда он интерпретируется/анализируется механизмом Javascript (во время этапа создания этапа)? то есть какой из них поднимается над другим? Я читал, что объявление функции имеет приоритет над объявлением переменной, но почему это так?

Кроме того, пожалуйста, обратитесь к первому фрагменту кода (но это относится к обоим случаям), является ли местоположение/адрес памяти «a», где он объявлен в строке 1 и строке 6, ТОЧНО одинаковым? А так как 'a' в обоих местах кода представляет объявление функции, каким будет значение 'a' в его ячейке памяти/адресе после синтаксического анализа строки 1 и строки 6. на этапе создания? Поскольку объявления функций «поднимаются» над объявлением переменных, означает ли это, что в строке 1 адрес памяти «a» указывает на объект функции «a», к концу создания фаза контекста выполнения?

В обоих фрагментах кода 'var a' не определено, поэтому почему является 't 'a' значением undefined к тому времени, когда мы достигнем фазы выполнения (у которой точка выполнения начинается в строке 6?) Разве движок Javascript просто говорит: «Ах, если мы позволим 'var a' 'перезаписывать' 'function a() {…}', она останется неопределенной и, следовательно, непригодной для использования. Принимая во внимание, что если мы позволим 'a' представлять указанную функцию, то, по крайней мере, пользователь может вызвать указанную функцию (даже до ее объявления, без получения ссылки на ошибку)". Это очень маловероятно, но звучит логично/идеально , может быть?

Итак, мои вопросы:

  1. В чем причина того, почему объявление функции перезаписывается или имеет приоритет над объявлением переменной? Это что-то, что мы просто принимаем на первый взгляд, потому что это реализация спецификаций ECMAScript, или мы можем объяснить такое поведение?

И

  1. Если возможно, не мог бы кто-нибудь ответить, что происходит в памяти, когда это происходит на каждом шагу?

Я пытался использовать свою интуицию в течение последних 8 часов (где я в конечном итоге попал в другое измерение других особенностей JavaScript, но это отдельная проблема), но я хотел бы знать, есть ли простое или конкретное объяснение или ответ на такое поведение.

Любая помощь в понимании всего этого будет высоко оценена.


comment
Вы работаете над набором домашних заданий, потому что этот вопрос очень похож на другой ваш вопрос, опубликованный не так давно.   -  person Andy    schedule 02.09.2017
comment
Я не думаю, что когда-либо видел такой вопрос домашнего задания раньше. Изначально я планировал задать этот вопрос в этих темах, но они оказались слишком длинными. Это связано, но не имеет ничего общего с этим вопросом, поэтому это отдельная ветка. Любое смешение могло бы запутать и отвлечь от полного понимания моих вопросов. В любом случае, дело не только в том, чтобы помочь себе, но и в том, чтобы помочь другим, у которых может возникнуть подобный вопрос в будущем... и, к счастью, я не нашел подобного вопроса, как этот - а вы?   -  person 151SoBad    schedule 02.09.2017
comment
В обоих фрагментах кода «var a» не определено — эээ, нет?   -  person Bergi    schedule 02.09.2017
comment
@Bergi Я не хотел слишком много писать в комментариях во фрагменте. Я имел в виду, что если «var a» был СОБСТВЕННЫМ (и не было никакой функции «a») до наступления фазы выполнения, var был бы неопределенным, верно? потому что это не имеет значения...   -  person 151SoBad    schedule 02.09.2017
comment
Да, a имело бы значение undefined, если бы не было объявления функции   -  person Bergi    schedule 02.09.2017
comment
Спасибо Берги. В таком случае, почему спецификация JS и MDN описывают «неопределенное» как значение? Это реализация движка, которая следует этому примеру? Является ли переменная, которая содержит «неопределенное» значение, ДЕЙСТВИТЕЛЬНО значением в памяти, которое не определено? Если да, то каково это значение? Я сомневаюсь, что это строка с надписью undefined. Это нуль (0x00)? это ноп (0x90)? Или это ничего, а просто представляет собой «государство»? Это то, о чем я всегда думал, - пока спецификация JS, MDN и множество потоков stackoverflow не описали это как «значение» - поэтому я интерпретировал это как в памяти неопределенная переменная имеет значение   -  person 151SoBad    schedule 02.09.2017
comment
@ 151SoBad Да, это значение (почему бы и нет?). То, как это реализовано в движках, не имеет значения (и, скорее всего, будет отличаться).   -  person Bergi    schedule 02.09.2017
comment
@Bergi, потому что из некоторых комментариев, представленных ниже, было сказано, что это не имеет значения. Оператор var a; означает, что я объявляю, что переменная с именем a существует, var a; не дает значения, поэтому это не влияет на значение a. Но я просто приму это.   -  person 151SoBad    schedule 02.09.2017
comment
@ 151SoBad Я сказал, что значение undefined является значением. Не то, чтобы var a действительно присваивал это переменной, когда оператор встречается в выполнении.   -  person Bergi    schedule 02.09.2017
comment
Опять же, чтобы уточнить, любая инициализация переменной (т. е. var a = 100, до выполнения) имеет объявление переменной (т. е. var a) и в то же время присваивает значение undefined. только до тех пор, пока во время выполнения не встретится оператор инициализации переменной, где ему будет присвоено значение 100. Это правильно? Таким образом, ваше утверждение о том, что var a назначается неопределенным, когда оператор встречается в выполнении, противоречит этому, разве ему не присваивается значение undefined < i>до выполнения, а не во время выполнения? Да, это семантика, но я просто хочу внести ясность в этот вопрос.   -  person 151SoBad    schedule 04.09.2017
comment
@151SoBad Опять же, я не сказал, что var a присваивается undefined, когда оператор встречается при выполнении. Да и в остальном все правильно.   -  person Bergi    schedule 04.09.2017
comment
Надеюсь, что эта ветка прояснит ситуацию для всех, у кого есть такие же проблемы и вопросы. Большое спасибо Берги.   -  person 151SoBad    schedule 04.09.2017


Ответы (2)


Мой вопрос: в обоих случаях, почему объявление функции «перезаписывает»/«затеняет» или имеет «приоритет» над объявлением переменной? Может кто-нибудь объяснить это поведение?

В любом фрагменте есть только одно место, где переменной a присваивается значение, и это объявление функции. Оператор var a; означает «Я объявляю, что переменная с именем a существует». Поскольку объявление функции сообщает о том, что она существует и присваивает ей значение, оператор var a; на самом деле не имеет никакого эффекта.

На самом деле, если мы обратимся к разделу 10.5.8 спецификации JavaScript, мы увидим что конечным результатом этого var a; является буквально ничего не делать, потому что переменная a уже объявлена ​​к моменту обработки объявления var a;.

Я понимаю, что они, возможно, одинаковы (с точки зрения интерпретатора) с точки зрения конечного результата из-за явления «подъема», но почему объявление функции или объявление переменной имеют приоритет над другим, когда оно интерпретируется/ анализируется движком Javascript на этапе создания?

Ничто здесь не имеет «приоритета». Существует только одна переменная с именем a. var a; не дает a значения, поэтому не влияет на значение a.

Кроме того, пожалуйста, обратитесь к первому фрагменту кода (но это относится к обоим случаям), является ли место в памяти / адрес «a», где он объявлен в строке 1 и строке 6, ТОЧНО одинаковым?

Существует только одна переменная a, и она принимает только одно значение, поэтому этот вопрос в основном бессмысленен, но, грубо говоря, сама переменная a будет существовать только в одном месте и будет ссылаться только на одно значение (место памяти), которое будет определением этой функции.

Поскольку объявления функций «поднимаются» над объявлением переменной, означает ли это, что в строке 1 «адрес памяти a указывает на объект функции «a» к концу фазы создания контекста выполнения?

Мои познания в фазе создания не так уж велики, но в ходе выполнения этого кода a всегда ссылается только на одно значение, как я уже говорил выше.

В обоих фрагментах кода «var a» не определено, так почему же «a» не становится значением undefined к моменту, когда мы достигаем фазы выполнения (точка выполнения которой начинается в строке 6?)

Смотрите мой первый и второй ответы.

В чем причина того, что объявление функции перезаписывается или имеет приоритет над объявлением переменной? И

Как я уже сказал, это не имеет «приоритета», но причина, по которой в JavaScript есть подъем функций, заключается в том, чтобы позволить людям вызывать функции из строки в коде до фактического определения функции, аналогично тому, как другие основные языки, такие как C # и Ява делаю.

Если возможно, может кто-нибудь ответить, что происходит в памяти, когда это происходит на каждом шагу?

Спецификация JavaScript не определяет, что происходит в памяти. Он определяет внешнее поведение кода. Таким образом, это будет деталь реализации и будет зависеть от двигателя.

Вот примерный пример того, что происходит, когда ваш код выполняется (в обоих случаях):

  1. Создайте переменную с именем a и присвойте ей значение той функции, которая у вас есть.
  2. Проверьте, есть ли переменная с именем a. Один уже есть, так что ничего не делайте.
  3. Выполните console.log и передайте ему значение a (это и есть эта функция)
person JLRishe    schedule 02.09.2017
comment
Интересно - спасибо за ответ. Я так понимаю, что на этапе создания ничему не присваивается значение до пошагового выполнения кода движком. Является ли это ложным для объектов (конечно, включая объекты-функции)? Кроме того, давайте просто скажем, что они были названы по-разному, то есть var a, function b(foo) { ...}, это разные области памяти, не так ли? Вы говорите, что есть только одна ячейка памяти, и в данном случае это переменная «b». А как насчет переменной «а»? Почему же тогда при компиляции мы не получаем ошибку ссылки, а вместо этого получаем undefined? - person 151SoBad; 02.09.2017
comment
@ 151SoBad Вообще говоря, в вашем примере a/b будут задействованы три области памяти: (1) место, где существует переменная a (2) место, где существует переменная b (3) местоположение значения b. Как я уже сказал выше (в несколько менее ясных терминах), в вашем исходном примере задействованы две области памяти: (1) расположение самой переменной a (2) расположение значения a (функции). - person JLRishe; 02.09.2017
comment
Я полностью согласен и полностью понимаю все, что вы сказали в своем комментарии. Мой вопрос тоже, возможно, был сформулирован менее ясно. Я понимаю, что существует только одна переменная a, которая находится в одном месте в памяти. Мой вопрос должен и может быть лучше сформулирован так: как значение этого адреса памяти изменяется по мере выполнения кода на этапе создания контекста выполнения? - person 151SoBad; 02.09.2017
comment
В дополнение, в аналогичной заметке, не могли бы вы сказать мне, какое значение переменной «a» будет в моем примере a/b в комментариях? (На самом деле это еще один из моих вопросов в другой ветке, на который в настоящее время все еще нет ответа, но, похоже, у вас есть некоторые знания о памяти, поэтому, если вы знаете, это будет очень признательно!) - person 151SoBad; 02.09.2017
comment
@ 151SoBad Я думаю, вы неправильно понимаете, как работает движок JavaScript. Нет ни пошагового выполнения строк, ни подъема, который каким-то образом переупорядочивает строки. Существует фаза синтаксического анализа, которая обнаруживает переменные, введенные в область, и при выполнении кода область создается как одна — со всеми переменными и их соответствующими начальными значениями (undefined для var, функции для function или вообще без для let и const). - person Bergi; 02.09.2017
comment
@ 151SoBad Если вы хотите узнать, какое значение имеет переменная, просто console.log ее или используйте отладчик, чтобы выполнить код. - person Bergi; 02.09.2017
comment
@ 151SoBad Опять же, я не совсем понимаю, что такое этап создания, поэтому я не мог ответить, что происходит на этапе создания. Что я могу сказать, основываясь на спецификации, так это то, что, поскольку для a существует объявление функции, оно аналогичным образом создается и получает переменную на шаге 5.f процедуры создания экземпляра привязки объявления, и после этого оно не принимает никакого значения. . - person JLRishe; 02.09.2017
comment
@151SoBad Кроме того, в аналогичной заметке, не могли бы вы сказать мне, какое значение переменной 'a' будет в моем примере a/b в комментариях? Значение будет undefined, потому что оно не присваивается значение. - person JLRishe; 02.09.2017
comment
@Bergi В комментарии, где я обсуждал пошаговое выполнение строки, я упомянул контекст, когда код выполняется. Это на самом деле не так? Я согласен и понимаю, что вы только что говорили о том, что происходит на этапе создания контекста выполнения, который по существу включает синтаксический анализ - именно то, что вы описали. - person 151SoBad; 02.09.2017
comment
@JLRishe Я не особо понимаю, что сказано на шаге 5.f, так как это слишком жаргонно для меня, так как мне нужно будет провести некоторое исследование SetMutableBinding. Но когда вы сказали, что после этого она не принимает никакого значения, это потому, что она становится неизменной, или просто потому, что после этого ничто не «переписывает» эту переменную в моем коде? Я предполагаю, конечно, последнее, но просто хотел подтвердить. - person 151SoBad; 02.09.2017
comment
@ 151SoBad Это последнее. Переменная остается полностью изменяемой. - person JLRishe; 02.09.2017

Объявление функции поднимается перед подъемом переменной.

console.log(a);

var a = 42; // assignment

console.log(a);

function a(foo) { // a function named 'a'
  var foo = "Hello World";
  console.log(foo);
}

console.log(a);

person Nina Scholz    schedule 02.09.2017
comment
Я полностью согласен, и я понимаю, что это совершенно другой сценарий, когда происходит задание. Я просто хочу знать, в сценарии, где присваивание не происходит, почему объявление функции поднимается до подъема переменной. Это что-то, что мы просто принимаем на первый взгляд, потому что это реализация спецификаций ECMAScript, или мы можем объяснить такое поведение? - person 151SoBad; 02.09.2017
comment
@151SoBad Я просто хочу знать, почему в сценарии, где присваивание не происходит, объявление функции поднимается до подъема переменной. Потому что именно так спецификация определяет поведение? Есть ли что-то, что вам нужно знать помимо этого? - person JLRishe; 02.09.2017
comment
@JLRishe, ну нет, это именно то, что мне было интересно. Кроме того, я также не смог найти ссылку, которая точно говорит об этом в спецификации, поэтому вопрос, который я только что задал в комментариях здесь. В дополнение к спецификации, насколько я знаю, разные реализации спецификации сохраняют разное поведение. Я не слишком уверен, что здесь с намеком на отношение. Да, мои вопросы могут быть не связаны конкретно с кодированием и, возможно, касаются будничной и тривиальной стороны вещей, но наверняка есть кто-то такой же любопытный (может быть, не на самом деле, но я не на том сайте, чтобы задавать эти вопросы? ). - person 151SoBad; 02.09.2017
comment
@JLRishe ищите все, что я знаю, я мог бы подойти к тому, как неправильно изучать Javascript и просто принимать вещи на первый взгляд, но в большинстве случаев я видел, как люди описывают явления и особенности этого языка, которые логически имеют смысл (и вещи, которые не имеют смысла, которые также не описаны в спецификации). Кажется, у некоторых может быть лучшее понимание реализации и того, как она работает, поэтому мне было интересно узнать, существует ли такое понимание. У меня нет технического образования, поэтому моя линия вопросов может показаться необычной, но это благие намерения. - person 151SoBad; 02.09.2017
comment
@ 151SoBad Да, подъем конкретно определен в спецификации (хотя на самом деле он не называется подъемом и определен в нескольких разных разделах спецификации), поэтому его наблюдаемые эффекты не должны отличаться от одной реализации к другой. Вот страница MDN, которая подробно объясняет подъем простым языком: developer.mozilla .org/en-US/docs/Glossary/Hoisting, а вот сообщение в блоге, в котором указаны все части спецификации, определяющие поведение подъема: johnkpaul.com/blog/2013/02/11/what-is-hoisting-really - person JLRishe; 02.09.2017
comment
Нет, я согласен с тем, что подъем определен в спецификации, хотя и не называется подъемом, но я не смог найти нигде, где обсуждалось бы поведение объявления функции перед объявлением переменной. Это могут быть интерпретации, но я видел множество источников, в которых говорится о том, чем отличается подъем для функциональных объявлений и объявлений переменных. Отметив это, я хотел знать, почему первое поднимается «перед» вторым (я понимаю, вы сказали, что ничто не имеет «приоритета» и что это не так), но почему множество источников обсуждают это? Что на самом деле происходит тогда? - person 151SoBad; 02.09.2017
comment
@151SoBad Посмотрите раздел 10.5 спецификации. Подъем функции происходит на шаге 5 этой процедуры. Подъем переменной происходит на шаге 8. Шаг 5 предшествует шагу 8, следовательно, сначала происходит подъем функции. - person JLRishe; 02.09.2017
comment
Спасибо - я посмотрю на это сейчас - только что отметил это в вашем сообщении выше. - person 151SoBad; 02.09.2017
comment
@ 151SoBad В своем ответе я попытался подчеркнуть, что на самом деле не имеет значения, что произойдет первым. Если бы это было в обратном порядке, наблюдаемый результат был бы таким же: var a; создала бы переменную с именем a, и объявление функции дало бы ей значение до того, как какие-либо операторы начали выполняться. И ваш console.log(a) все равно будет регистрировать то же значение. - person JLRishe; 02.09.2017
comment
@JLRishe спасибо за повторение этого. Я полностью согласен. На самом деле, если я правильно вас интерпретирую, кажется, я упомянул об этом сразу в конце своих фрагментов. Я понимаю, что оба достигают одного и того же результата из-за подъема. Другими словами, игнорируйте фрагмент 2, фрагмента 1 будет достаточно, чтобы задать мой вопрос. Я был сбит с толку тем, почему это привело к тому, что переменная «а» ссылается на функцию «а», а не на переменную данных «а». Я полагаю, что мое замешательство произошло из-за того, что вы указали, что объявления func приводят к значению, присвоенному переменной, тогда как объявление переменных - нет. Это правильно? - person 151SoBad; 02.09.2017
comment
Этот вопрос, связанный с присвоением значения в зависимости от того, является ли это объявлением функции или объявлением переменной, был моим первым комментарием к вашему проницательному ответу. Думаю, ответ на этот вопрос развеет мою главную тревогу. Фактически, как упоминалось ранее, я обсуждал другую сторону этого здесь: stackoverflow.com/questions/46013456/ - person 151SoBad; 02.09.2017
comment
Пока нет ответа, но я подумал, что, поскольку спецификация Javascript (а также MDN) определяет «неопределенное» как примитивное ЗНАЧЕНИЕ, да, оно определено как ЗНАЧЕНИЕ, поэтому я понял, что это означает ДА, что переменная, которой не присвоено значение, будет 'undefined' и, таким образом, В ПАМЯТИ БУДЕТ присвоено значение. Итак, мой вопрос в этой теме был... что это за значение именно в памяти? Однако из ваших комментариев следует, что НЕТ ЗНАЧЕНИЯ для объявлений переменных, это правильно? Если да, то гений! Пожалуйста, дай мне знать. Мои извинения за заглавные буквы, здесь нет BBcode, поэтому я не могу выделить жирным шрифтом и должен полагаться на заглавные буквы. - person 151SoBad; 02.09.2017
comment
@ 151SoBad Моя точка зрения заключалась не в том, что ваши два примера кода эквивалентны, а в том, что даже если бы спецификация была написана таким образом, что подъем переменной происходил первым, в ней не было бы заметной разницы по сравнению с реальной ситуацией, когда происходит подъем функции. место первое. - person JLRishe; 02.09.2017
comment
@JLRishe еще раз, спасибо, что повторили это, чтобы подчеркнуть эту точку зрения. Однако предпосылка вашей точки зрения основана на более фундаментальной идее, о которой я только что спросил. То есть предполагается, что переменной в памяти не присваивается произвольное значение, представляющее «неопределенное» (как несколько намекает спецификация ES и т. д.)? Другими словами, если бы значение было присвоено в памяти переменной 'a', ваша точка зрения была бы неверной, верно? Я думаю, что после того, как мы ответим на это (что только для того, чтобы развеять мое собственное замешательство, а, скорее всего, не ваше), ваши пункты будут иметь ИДЕАЛЬНЫЙ смысл. - person 151SoBad; 02.09.2017
comment
@ 151SoBad Я сказал, что не будет заметной разницы, например, вы не сможете написать код, который будет иметь разные выходные данные в области подъема функции и первой земли переменной. Переменная может на мгновение принять значение undefined в последнем случае (и, по сути, undefined является значением), но недостаточно долго, чтобы вы это заметили. - person JLRishe; 02.09.2017
comment
Разве ты не сказал в своем исходном сообщении, что это не имеет значения? Оператор var a; означает, что я объявляю, что переменная с именем a существует, var a; не дает значения, поэтому это не влияет на значение a.? Я немного смущен сейчас. Кроме того, в приведенном выше примере, в любом случае func-hoisting-first land и var-hoisting-first land, вместо фрагмента 1, если мы посмотрим на фрагмент 2, не будет ли это означать, что переменная принимает значение на мгновение, прежде чем он в конечном итоге примет значение undefined, где он выводится на консоль? Таким образом, это БУДЕТ сделать diff ч / б 2 фрагмента? - person 151SoBad; 02.09.2017