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

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

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

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

f();

Выход, который я получаю, - "Я оригинален". Почему другая функция не вызывалась?

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

Ответ 1

Объявления функций поднимаются (перемещаются вверху) в 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();

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

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

Ответ 2

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

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

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 не является функцией.

Ответ 3

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

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

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

f();

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

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

f();

Выход будет следующим:

Me duplicate.
Me original.

Причина заключается в том, что инструкции var и function поднимаются несколько разными способами.

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

В выражениях 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 в этой точке.