Почему имена моих функций JavaScript конфликтуют?

97

Я написал следующий сценарий, чтобы посмотреть, что происходит, когда имена переменной и функции, которой назначена функция, совпадают:

var f = function() {
    console.log("Me original.");
}

function f() {
    console.log("Me duplicate.");
}

f();

В результате я получаю «Я оригинальный». Почему не была вызвана другая функция?

Кроме того, если я изменяю свое исходное назначение на var f = new function() {, я получаю «Я исходный», за которым следует сообщение TypeError object is not a function. Кто-нибудь может объяснить?

анкуш981
источник
26
@ Dean.DePue - Со стороны JavaScript нет путаницы. Правила обращения с ними довольно ясны (и объяснены Бенджамином в своем ответе).
Квентин
4
Любопытство - по-прежнему лучший способ выучить язык. :-D
Цербрус
2
Кроме того, я полагаю, что для чего-то столь несущественного, как "JavaScript", невозможно "почувствовать" смущение (или какие-либо эмоции, если на то пошло) ;-)
Cerbrus
2
Почему во втором примере при подъеме следует изменить порядок?
Cerbrus
5
Шаги для углубления знаний о javascript: 1) Используйте 'use strict' 2) Всегда используйте либо jslint, либо jshint 3) Найдите то, на что jslint или jshint жалуется 4) Промойте и повторите
Стив-Эр-Рино

Ответы:

170

Объявления функций поднимаются (перемещаются вверх) в JavaScript. Код, который у вас неверен с точки зрения порядка синтаксического анализа, семантически совпадает с приведенным ниже, поскольку объявления функций поднимаются:

function f() {
    console.log("Me duplicate.");
}
var f = function() {
    console.log("Me original.");
}


f();

Что, в свою очередь, за исключением имени функции совпадает с:

var f = function() {
    console.log("Me duplicate.");
}
var f = function() {
    console.log("Me original.");
}


f();

Что, в свою очередь, из-за подъема переменных совпадает с:

var f;
f = function() {
    console.log("Me duplicate.");
}
f = function() {
    console.log("Me original.");
}

f();

Это объясняет, что вы получаете, вы отменяете функцию. В общем, varв JavaScript разрешено несколько объявлений - var x = 3; var x = 5это совершенно законно. В новом стандарте ECMAScript 6 letэто запрещено операторами.

Эта статья @kangax делает фантастическую работу по демистификации функций в javascript.

Бенджамин Грюнбаум
источник
2
Можете ли вы действительно упростить function f()для var f = function()так много? Действительно ли подъем и имена функций - единственная разница?
djechlin
6
@djechlin в контексте этого вопроса - да. Как правило, он более тонкий - см. Stackoverflow.com/questions/336859/… . С точки зрения компилятора они разные, но с точки зрения программиста мы достаточно близки к этому, чтобы утверждать это. Вот почему я добавил это длинное выражение «хотя и неверно с точки зрения порядка анализа, код, который у вас есть, семантически такой же, как», вместо того, чтобы сказать «такой же, как». Хорошая точка зрения.
Бенджамин Грюнбаум
1
@dotslash, пожалуйста, не редактируйте исходный вопрос и не меняйте его, это считается здесь дурным тоном. Кроме того, смешивание нескольких вопросов в одном здесь также считается дурным тоном. Вместо этого вы можете задать новый вопрос или, если вы считаете, что он слишком незначительный, попросить разъяснений в комментариях (в любом случае они для этого). В приведенном выше коде обе версии fподнимаются, а "Me Original"версия просто поднимается позже , каждая из них перемещается наверх, но в том же порядке. Я просто хотел бы добавить, что, как правило, нельзя называть несколько функций одинаково :)
Бенджамин Грюнбаум
5
В строгом режиме вы не можете varиспользовать одно и то же имя дважды в одной и той же области.
Hoffmann
4
«Это должно быть очевидно» - возможно, для вас , но в какой-то момент это было неочевидно для меня, и OP, когда он спросил об этом, не было очевидным, и наименование, и в целом, как управление лексической средой в JavaScript было одним из самых сложных вещей, которые нужно понять, когда я впервые изучал JavaScript. Я бы не стал так быстро оскорблять людей, которые этого не понимают.
Бенджамин Грюнбаум
10

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

Вы спросили, почему это:

var f = new function() {
    console.log("Me original.");
}

function f() {
    console.log("Me duplicate.");
}

f();

распечатывает "Я оригинал". а потом ошибка.

Здесь происходит то, что newэта функция используется в качестве конструктора. Это эквивалентно следующему:

function myConstructor() {
    console.log("Me original.");
}
var f = new myConstructor();

function f() {
    console.log("Me duplicate.");
}

f();

И благодаря функции подъема, которую объяснил Бенджамин, приведенное выше по существу эквивалентно следующему:

var myConstructor = function() {
    console.log("Me original.");
};
var f = function() {
    console.log("Me duplicate.");
};

f = new myConstructor();

f();

Это выражение:

var f = new function() {
    console.log("Me original.");
}

вызывает создание и назначение нового объекта с fиспользованием анонимной функции в качестве конструктора. "Я оригинален". распечатывается по мере выполнения конструктора. Но созданный объект сам по себе не является функцией, поэтому, когда это в конечном итоге выполняется:

f();

вы получите сообщение об ошибке, потому что fэто не функция.

JLRishe
источник
Ой, прекрасно! Спасибо, что потрудились ответить на него! :) :)
ankush981 04
2

Простите меня, если это неправильный подход к добавлению точки. Я нечасто бывал здесь и приветствовал бы конструктивные указания и / или критику.

Ответ Бенджамина отлично отвечает на вопрос ОП, но я хотел бы добавить одну настройку, которая даст нам полный обзор подъема и его странностей.

Если мы начнем исходный код с вызова f, например:

f();

var f = function() {
   console.log("Me original.");
};

function f() {
   console.log("Me duplicate.");
}

f();

Результатом будет:

Me duplicate.
Me original.

Причина в том , что varи functionзаявления водрузили по - разному.

Для varв декларации перемещаются в начало текущей области видимости *, но любое задание не поднимается. Что касается значения объявленной переменной, оно не определено до тех пор, пока не будет достигнута исходная строка присвоения.

Для functionоператоров поднимаются и объявление, и определение. Функциональные выражения , используемые в var f = function() {...конструкции, не поднимаются.

Итак, после подъема выполнение выглядит так, как если бы код был:

var f; // declares var f, but does not assign it.

// name and define function f, shadowing the variable
function f() { 
  console.log("Me duplicate.");
}

// call the currently defined function f
f(); 

// assigns the result of a function expression to the var f,
// which shadows the hoisted function definition once past this point lexically
f = function() { 
  console.log("Me original."); 
}

// calls the function referenced by the var f
f();

* Вся область видимости JavaScript является лексической, или функцией, областью видимости, но казалось, что использование слова f в этот момент просто сбивает с толку.

Codelahoma
источник