Чому мої функції імен 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. Може хтось, будь ласка, пояснить?


26
@ Dean.DePue - Немає плутанини з боку JavaScript. Правила поводження з ними досить чіткі (і пояснив Бенджамін у своїй відповіді).
Квентін

4
Цікавість, як і раніше найкращий спосіб дізнатися про мову. :-D
Цербр

2
Крім того, я думаю, що неможливо, щоб щось таке несуттєве, як "JavaScript", "почувалося" розгубленим (або будь-яка емоція, з цього приводу) ;-)
Cerbrus

2
Чому в другому прикладі вантажопідйомник повинен змінювати наказ?
Чербрус

5
Етапи для вдосконалення знань JavaScript: 1) Використовувати "використовувати строгий" 2) Завжди користуватися або jslint, або jshint 3) Знайдіть речі, на які jslint чи jshint скаржиться на 4) промийте та повторіть
steve-er-rino

Відповіді:


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 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двічі одне і те ж ім’я двічі в одній області застосування.
Гофман

4
"Це повинно бути очевидним" - вам, можливо, але мені це було не очевидно, і ОП не було очевидним, коли він запитував це, і називаючи, і загалом, як лексичне середовище в 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це не функція.


О, чудово! Дуже дякую, що взяли на себе проблеми, щоб відповісти на це! :) :)
ankush981

2

Пробачте, якщо це неправильний спосіб підійти до додавання балів. Я не був тут дуже багато, і я вітаю конструктивне керівництво та / або критику.

Відповідь Бенджаміна чудово відповідає на питання ОП, але я хотів би додати ще один виправлення, яке дасть нам повну подорож підйомного обладнання та його дивацтва.

Якщо ми починаємо оригінальний код із заклику до f, так:

f();

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

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

f();

Вихід тоді буде:

Me duplicate.
Me original.

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

Для varв декларації переміщаються на початок поточного області видимості *, але будь-який завдання не піднімається. Що стосується значення оголошеного 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 у цьому пункті.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.