var functionName = function () {} vs function functionName () {}


6871

Нещодавно я почав підтримувати чужий код JavaScript. Я виправляю помилки, додаю функції, а також намагаюся виправити код і зробити його більш послідовним.

Попередній розробник використовував два способи оголошення функцій, і я не можу розробитись, чи є причина для цього чи ні.

Два способи:

var functionOne = function() {
    // Some code
};
function functionTwo() {
    // Some code
}

Які причини використання цих двох різних методів та які плюси та мінуси кожного? Чи є щось, що можна зробити одним методом, що не можна зробити з іншим?


198
permadi.com/tutorial/jsFunc/index.html - дуже хороша сторінка про функції javascript
uzay95

67
Пов’язана ця чудова стаття про вираження іменованих функцій .
Фрогз

12
@CMS посилається на цю статтю: kangax.github.com/nfe/#expr-vs-decl
Upperstage

106
Ви повинні пам’ятати про дві речі: №1 У JavaScript декларації піднімаються. Сенс, який var a = 1; var b = 2;стає var a; var b; a = 1; b = 2. Отже, коли ви оголошуєте functionOne, він оголошується, але його значення встановлюється не відразу. Оскільки functionTwo є лише декларацією, вона ставиться у верхній частині області. Функція №2Двома дозволяє отримати доступ до властивості імені, і це дуже допомагає при спробі налагодити щось.
xavierm02

65
О і btw, правильний синтаксис є з ";" після присвоєння і без декларації. Наприклад , function f(){}проти var f = function(){};.
xavierm02

Відповіді:


5043

Різниця полягає в тому, що functionOneце вираження функції, яке визначається лише тоді, коли буде досягнуто цього рядка, тоді functionTwoяк оголошення функції і визначається, як тільки виконується його оточуюча функція або сценарій (за рахунок підняття ).

Наприклад, вираз функції:

// TypeError: functionOne is not a function
functionOne();

var functionOne = function() {
  console.log("Hello!");
};

І, оголошення функції:

// Outputs: "Hello!"
functionTwo();

function functionTwo() {
  console.log("Hello!");
}

Історично декларації функцій, визначені в блоках, оброблялися непослідовно між браузерами. Суворий режим (запроваджений в ES5) вирішив це шляхом визначення обсягу оголошень функції до блоку, що їх додає.

'use strict';    
{ // note this block!
  function functionThree() {
    console.log("Hello!");
  }
}
functionThree(); // ReferenceError


631
@Greg: До речі, різниця полягає не лише в тому, що вони розбираються в різний час. По суті, ви functionOneє просто змінною, яка присвоює їй анонімну функцію, тоді functionTwoяк насправді це названа функція. Зателефонуйте .toString()обом, щоб побачити різницю. Це важливо в деяких випадках, коли ви хочете отримати ім'я функції програмно.
Джейсон Бантінг

6
@Jason Bunting .. не впевнений, що ви тут отримуєте, .toString (), схоже, повертає однаково значення (визначення функції) для обох: cl.ly/2a2C2Y1r0J451o0q0B1B
Jon z

124
Є обидва різні. Перший - function expressionдругий - а function declaration. Більше про цю тему можна прочитати тут: javascriptweblog.wordpress.com/2010/07/06/…
Michal Kuklis

127
@Greg Частина вашої відповіді щодо часу розбору та часу виконання не відповідає правильності. У JavaScript декларації функцій визначаються не під час аналізу, а під час виконання. Процес проходить так: Аналіз вихідного коду -> Оцінюється програма JavaScript -> Ініціалізується глобальний контекст виконання -> Ініціюється прив'язка декларації. Під час цього процесу декларації функцій ініціюються (див. Крок 5 глави 10.5 ).
Šime Vidas

102
Термінологія цього явища відома як підйом.
Колін Груш

1941

По-перше, я хочу виправити Грега: також function abc(){}розміщено обсяг - ім'я abcвизначається в тій області, де зустрічається це визначення. Приклад:

function xyz(){
  function abc(){};
  // abc is defined here...
}
// ...but not here

По-друге, можна комбінувати обидва стилі:

var xyz = function abc(){};

xyzбуде визначено як завжди, abcне визначено у всіх браузерах, окрім Internet Explorer - не покладайтеся на те, що це буде визначено. Але все це буде визначено всередині його тіла:

var xyz = function abc(){
  // xyz is visible here
  // abc is visible here
}
// xyz is visible here
// abc is undefined here

Якщо ви хочете отримати функції псевдоніму у всіх браузерах, використовуйте такий вид декларації:

function abc(){};
var xyz = abc;

У цьому випадку обидва xyzі abcє псевдонімами одного і того ж об'єкта:

console.log(xyz === abc); // prints "true"

Однією з вагомих причин використовувати комбінований стиль є атрибут "name" функціональних об'єктів ( не підтримується Internet Explorer ). В основному, коли ви визначаєте функцію типу

function abc(){};
console.log(abc.name); // prints "abc"

його ім’я присвоюється автоматично. Але коли ти це визначаєш як

var abc = function(){};
console.log(abc.name); // prints ""

його ім’я порожнє - ми створили анонімну функцію і призначили її якійсь змінній.

Ще однією вагомою причиною використання комбінованого стилю є використання короткого внутрішнього імені для позначення себе, надаючи довге неконфліктне ім’я для зовнішніх користувачів:

// Assume really.long.external.scoped is {}
really.long.external.scoped.name = function shortcut(n){
  // Let it call itself recursively:
  shortcut(n - 1);
  // ...
  // Let it pass itself as a callback:
  someFunction(shortcut);
  // ...
}

У наведеному вище прикладі ми можемо зробити те ж саме із зовнішньою назвою, але це буде занадто громіздко (і повільніше).

(Ще один спосіб посилатися на себе - це використання arguments.callee, яке все ще є досить тривалим і не підтримується в суворому режимі.)

У глибині душі JavaScript трактує обидва твердження по-різному. Це декларація функції:

function abc(){}

abc тут визначено всюди в поточному масштабі:

// We can call it here
abc(); // Works

// Yet, it is defined down there.
function abc(){}

// We can call it again
abc(); // Works

Крім того, це було зроблено через returnзаяву:

// We can call it here
abc(); // Works
return;
function abc(){}

Це вираження функції:

var xyz = function(){};

xyz тут визначено з точки призначення:

// We can't call it here
xyz(); // UNDEFINED!!!

// Now it is defined
xyz = function(){}

// We can call it here
xyz(); // works

Оголошення функції проти вираження функції - це реальна причина, чому існує різниця, продемонстрована Грегом.

Смішний факт:

var xyz = function abc(){};
console.log(xyz.name); // Prints "abc"

Особисто я віддаю перевагу декларації "вираз функції", оскільки таким чином я можу контролювати видимість. Коли я визначаю функцію, як

var abc = function(){};

Я знаю, що я визначив функцію локально. Коли я визначаю функцію, як

abc = function(){};

Я знаю, що я визначив це глобально, за умови, що я не визначав abcніде в ланцюжку областей. Цей стиль визначення є стійким навіть при використанні всередині eval(). Поки визначення

function abc(){};

залежить від контексту і може залишити вас здогадуватися, де це насправді визначено, особливо у випадку eval()- відповідь така: Це залежить від браузера.


69
Я посилаюся на RoBorg, але його ніде не знайти. Просто: RoBorg === Грег. Ось як можна переписати історію в епоху Інтернету. ;-)
Євген Лазуткін

10
var xyz = функція abc () {}; console.log (xyz === abc); Усі перевірені браузерами (Safari 4, Firefox 3.5.5, Opera 10.10) дають мені "Невизначена змінна: abc".
NVI

7
В цілому, я думаю, що цей пост добре допомагає пояснити відмінності та переваги використання декларації функції. Я погоджуся не погоджуватися, що стосується переваг використання присвоєнь функцій виразам змінній, тим більше, що "вигода" є пропагандою декларування глобальної сутності ... і всі знають, що не варто захаращувати глобальний простір імен , правда? ;-)
natlee75

83
imo величезна причина використовувати названу функцію, тому що налагоджувачі можуть використовувати це ім'я, щоб допомогти вам зрозуміти стек виклику або слід стека. це смокче, коли дивишся на стек викликів і бачиш "анонімну функцію" глибиною на 10 рівнів ...
коза

3
var abc = function(){}; console.log(abc.name);більше не виробляє "", а "abc"натомість.
Qwerty

632

Ось перелік стандартних форм, що створюють функції: (Спочатку написано для іншого питання, але адаптовано після переходу в канонічне запитання.)

Умови:

Швидкий список:

  • Декларація функції

  • "Анонімний" functionвираз (який, незважаючи на термін, іноді створює функції з іменами)

  • Іменований functionвираз

  • Ініціалізатор функцій аксесуара (ES5 +)

  • Експресія функції стрілки (ES2015 +) (яка, як і анонімні вирази функцій, не передбачає явного імені, але все ж може створювати функції з іменами)

  • Декларація методу в ініціалізаторі об'єктів (ES2015 +)

  • Декларації конструктора та методу в class(ES2015 +)

Декларація функції

Перша форма - це оголошення функції , яке виглядає приблизно так:

function x() {
    console.log('x');
}

Оголошення функції - це декларація ; це не твердження чи вираз. Таким чином, ви не дотримуєтесь цього ;(хоча це нешкідливо).

Оголошення функції обробляється, коли виконання входить в контекст, в якому воно з'являється, перед тим , як виконувати будь-який покроковий код. Функція, яку вона створює, отримує власне ім'я ( xу прикладі вище), і це ім'я вводиться в область, в якій з'являється декларація.

Оскільки він обробляється перед будь-яким покроковим кодом у тому ж контексті, ви можете робити такі дії:

x(); // Works even though it's above the declaration
function x() {
    console.log('x');
}

До ES2015, специфікація НЕ не охоплюють то , що двигун JavaScript повинен робити , якщо помістити оголошення функції всередині структури управління , як try, if, switch, whileі т.д., як це:

if (someCondition) {
    function foo() {    // <===== HERE THERE
    }                   // <===== BE DRAGONS
}

А оскільки вони обробляються до запуску покрокового коду, важко знати, що робити, коли вони знаходяться в структурі управління.

Хоча це не було вказано до ES2015, це було допустимим розширенням на підтримку декларацій функцій в блоках. На жаль (і неминуче) різні двигуни робили різні речі.

Станом на ES2015 специфікація говорить, що робити. Насправді це дає три окремі речі:

  1. Якщо у вільному режимі немає веб-браузера, механізм JavaScript повинен зробити одне
  2. Якщо у вільному режимі у веб-браузері, механізм JavaScript повинен робити щось інше
  3. Якщо в суворому режимі (браузер чи ні), двигун JavaScript повинен зробити ще одну справу

Правила для вільних режимів складні, але в суворому режимі декларації функцій в блоках прості: вони локальні для блоку (вони мають область блоку , що також є новим у ES2015), і вони піднімаються до верху блоку. Тому:

"use strict";
if (someCondition) {
    foo();               // Works just fine
    function foo() {
    }
}
console.log(typeof foo); // "undefined" (`foo` is not in scope here
                         // because it's not in the same block)

"Анонімний" functionвираз

Друга поширена форма називається виразом анонімної функції :

var y = function () {
    console.log('y');
};

Як і всі вирази, вона оцінюється, коли вона досягається при поетапному виконанні коду.

У ES5 створена функція не має імені (це анонімне). У ES2015 функції, якщо це можливо, присвоюється ім'я шляхом виведення його з контексту. У наведеному вище прикладі назва y. Щось подібне робиться, коли функцією є значення ініціалізатора властивостей. (Докладніше про те, коли це відбувається, і правила, знайдіть SetFunctionNameу специфікації  - вона з’являється в усьому місці.)

Іменований functionвираз

Третя форма - це виражений функцією (NFE):

var z = function w() {
    console.log('zw')
};

Функція, яку вона створює, має власне ім’я ( wу цьому випадку). Як і всі вирази, це оцінюється, коли воно досягається при поетапному виконанні коду. Ім'я функції не додається до сфери, в якій з'являється вираз; ім'я знаходиться в межах сфери в межах самої функції:

var z = function w() {
    console.log(typeof w); // "function"
};
console.log(typeof w);     // "undefined"

Зауважте, що NFE часто були джерелом помилок для реалізації JavaScript. Наприклад, IE8 та новіші версії, наприклад, обробляють NFE повністю неправильно , створюючи дві різні функції у два різні періоди. Ранні версії Safari також мали проблеми. Хороша новина полягає в тому, що поточні версії браузерів (IE9 і новіших версій, поточний Safari) вже не мають цих проблем. (Однак, на жаль, IE8 залишається широко використовуваним, і тому використання NFE з кодом для Інтернету взагалі залишається проблематичним.)

Ініціалізатор функцій аксесуара (ES5 +)

Іноді функції можуть прокрадатись майже непомітно; це справа з функціями аксесуара . Ось приклад:

var obj = {
    value: 0,
    get f() {
        return this.value;
    },
    set f(v) {
        this.value = v;
    }
};
console.log(obj.f);         // 0
console.log(typeof obj.f);  // "number"

Зауважте, що коли я використовував цю функцію, я не користувався ()! Це тому, що це функція аксесуара для властивості. Ми отримуємо та встановлюємо властивість у звичайному порядку, але поза кадром функція викликається.

Ви також можете створити функції доступу з Object.defineProperty, Object.definePropertiesі менш відомим другим аргументом Object.create.

Вираження функції стрілки (ES2015 +)

ES2015 пропонує нам функцію стрілки . Ось один приклад:

var a = [1, 2, 3];
var b = a.map(n => n * 2);
console.log(b.join(", ")); // 2, 4, 6

Бачите цю n => n * 2річ, що ховається у map()дзвінку? Це функція.

Кілька речей про функції стрілок:

  1. У них немає своїх this. Замість цього, вони близько надthis з контексту , в якому вони визначені. (Вони також закриваються argumentsі, де це доречно,. super) Це означає, що thisвсередині них те саме, що thisтам, де вони створені, і не можуть бути змінені.

  2. Як ви помітили з вищесказаним, ви не використовуєте ключове слово function; натомість ви використовуєте =>.

Наведений n => n * 2вище приклад - одна з них. Якщо у вас є кілька аргументів для передачі функції, ви використовуєте паролі:

var a = [1, 2, 3];
var b = a.map((n, i) => n * i);
console.log(b.join(", ")); // 0, 2, 6

(Пам'ятайте, що Array#mapзапис передається як перший аргумент, а індекс - як другий.)

В обох випадках тіло функції - це лише вираз; повернене значення функції автоматично буде результатом цього виразу (ви не використовуєте явне return).

Якщо ви робите більше, ніж просто один вираз, використовуйте {}та явне return(якщо вам потрібно повернути значення), як звичайне:

var a = [
  {first: "Joe", last: "Bloggs"},
  {first: "Albert", last: "Bloggs"},
  {first: "Mary", last: "Albright"}
];
a = a.sort((a, b) => {
  var rv = a.last.localeCompare(b.last);
  if (rv === 0) {
    rv = a.first.localeCompare(b.first);
  }
  return rv;
});
console.log(JSON.stringify(a));

Версія без { ... }називається функцією стрілки з виразом тіла або стислого тіла . (Також: Коротка функція стрілки.) Функція, що { ... }визначає тіло, - це функція стрілки з тілом функції . (Також: Докладна функція стрілки.)

Декларація методу в ініціалізаторі об'єктів (ES2015 +)

ES2015 дозволяє коротшу форму оголошення властивості, на яку посилається функція, що називається визначенням методу ; це виглядає приблизно так:

var o = {
    foo() {
    }
};

майже еквівалентним у ES5 і раніше був би:

var o = {
    foo: function foo() {
    }
};

відмінність (крім багатослів’я) полягає в тому, що метод може використовуватись super, але функція не може. Так, наприклад, якби у вас був об'єкт, який визначив (скажімо), valueOfвикористовуючи синтаксис методу, він міг би використати super.valueOf()для отримання значення, Object.prototype.valueOfяке повернулося б (перш ніж, мабуть, зробити щось інше з ним), тоді як версія ES5 повинна зробити Object.prototype.valueOf.call(this)замість цього.

Це також означає, що метод має посилання на об'єкт, на який він був визначений, тому якщо цей об'єкт тимчасовий (наприклад, ви передаєте його Object.assignяк один із вихідних об'єктів), синтаксис методу може означати, що об'єкт зберігається в пам’яті, коли в іншому випадку це могло бути зібрано сміття (якщо двигун JavaScript не виявить цю ситуацію і не впорається з нею, якщо жоден із методів не використовує super)

Декларації конструктора та методу в class(ES2015 +)

ES2015 пропонує нам classсинтаксис, включаючи заявлені конструктори та методи:

class Person {
    constructor(firstName, lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }

    getFullName() {
        return this.firstName + " " + this.lastName;
    }
}

Вгорі є дві декларації функції: Одна для конструктора, яка отримує ім'я Person, і одна для getFullName, якій призначена функція Person.prototype.


3
то ім’я wпросто ігнорується?
BiAiB

8
@PellePenna: Назви функцій корисні для багатьох речей. На мій погляд, дві великі позиції - це рекурсія, а назва функції, яка відображається у стеках викликів, сліди винятку тощо.
TJ Crowder

4
@ChaimEliyah - "Прийняття не означає, що це найкраща відповідь, це просто означає, що це працювало для того, хто запитав". джерело
ScrapCode

6
@AR: Цілком правда. Цікаво, однак прямо над цим написано: "Найкращі відповіді з’являються спочатку, щоб їх завжди було легко знайти". Оскільки прийнята відповідь виявляється спочатку навіть над голосовими відповідями, тур може бути дещо суперечливим. ;-) Крім того, трохи неточно, якщо ми визначаємо "найкраще" за голосами (що не є надійним, це саме те, що ми отримали), "найкращі" відповіді з’являються лише спочатку, якщо ви використовуєте вкладку "Голоси" - інакше відповіді, які спочатку є активними, або найдавнішими.
TJ Crowder

1
@TJCrowder: Погоджено. "Упорядкована дата" іноді дратує.
ScrapCode

143

Якщо говорити про глобальний контекст, обидва, і varоператор, і a FunctionDeclarationв кінці створять властивість, що не видаляється, на глобальному об'єкті, але значення обох можна перезаписати .

Тонка різниця між двома способами полягає в тому, що коли запуститься процес змінної інстанції (до виконання фактичного коду), всі ідентифікатори, оголошені з, varбудуть ініціалізовані undefined, і ті, які використовуються, FunctionDeclarationбудуть доступні з цього моменту, наприклад:

 alert(typeof foo); // 'function', it's already available
 alert(typeof bar); // 'undefined'
 function foo () {}
 var bar = function () {};
 alert(typeof bar); // 'function'

Присвоєння місця bar FunctionExpressionвідбувається до часу виконання.

Глобальну властивість, створену a, FunctionDeclarationможна без проблем перезаписати так само, як і значення змінної, наприклад:

 function test () {}
 test = null;

Ще одна очевидна відмінність між вашими двома прикладами полягає в тому, що перша функція не має імені, а друга має її, що може бути дуже корисно при налагодженні (тобто, перевірка стека викликів).

Щодо вашого відредагованого першого прикладу ( foo = function() { alert('hello!'); };), це незадеклароване завдання, настійно рекомендую вам завжди використовувати varключове слово.

З присвоєнням без varзаяви, якщо посиланий ідентифікатор не знайдений у ланцюзі області, він стане властивістю, що видаляється глобальним об'єктом.

Крім того, незадекларовані завдання кидають ReferenceErrorна ECMAScript 5 у строгому режимі .

Потрібно прочитати:

Примітка . Ця відповідь була об'єднана з іншого запитання , в якому основним сумнівом і помилковим уявленням від ОП було те, що ідентифікатори, оголошені символом a FunctionDeclaration, не можуть бути перезаписані, що не відбувається.


Я не знав, що функції можуть бути перезаписані в JavaScript! Крім того, цей порядок розбору є для мене найбільшою точкою продажу. Я думаю, мені потрібно спостерігати, як я створюю функції.
Xeoncross

2
+0 до статті "Демістифіковані вирази функції імен", оскільки це 404ing. Можливе дзеркало ?: kangax.github.com/nfe
Mr_Chimp

@CMS Гарний. Майте на увазі, хоча я ніколи не бачив оригіналу, тому не знаю, чи це дзеркало чи просто інша стаття з такою ж назвою!
Mr_Chimp

@Mr_Chimp Я майже впевнений, що це так, thewaybackmachine каже, що він отримав 302 під час сканування, і перенаправлення було на вказане вами посилання.
Джон

123

Два фрагменти коду, які ви розмістили там, майже у всіх цілях будуть вести себе однаково.

Однак різниця в поведінці полягає в тому, що з першим варіантом ( var functionOne = function() {}) цю функцію можна викликати лише після цього пункту в коді.

З другим варіантом ( function functionTwo()) функція доступна для коду, який працює вище, де функція оголошена.

Це тому, що з першим варіантом функція присвоюється змінній fooпід час виконання. По-друге, функція призначається цьому ідентифікатору foo, в момент розбору.

Більше технічної інформації

У JavaScript є три способи визначення функцій.

  1. Ваш перший фрагмент показує вираз функції . Це передбачає використання оператора "function" для створення функції - результат цього оператора може бути збережений у будь-якій змінній або властивості об'єкта. Вираз функції таким чином потужний. Вираз функції часто називають "анонімною функцією", оскільки воно не має мати імені,
  2. Ваш другий приклад - оголошення функції . При цьому використовується оператор "function" для створення функції. Функція стає доступною в час розбору і може бути викликана в будь-якому місці цієї області. Ви можете зберігати його у змінній або властивості об'єкта пізніше.
  3. Третій спосіб визначення функції - це конструктор "Function ()" , який не показаний у вашій початковій публікації. Використовувати це не рекомендується, оскільки він працює так само, як eval()і у якого є свої проблеми.

103

Краще пояснення відповіді Грега

functionTwo();
function functionTwo() {
}

Чому немає помилки? Нас завжди вчили, що вирази виконуються зверху вниз (??)

Тому що:

hoistedІнтерпретатор JavaScript завжди (незмінно) переміщує декларації функцій та оголошення змінної вершини, що містить область їх застосування. Параметри функції та визначені мовою імена, очевидно, вже є. Бенрішрі

Це означає, що такий код:

functionOne();                  ---------------      var functionOne;
                                | is actually |      functionOne();
var functionOne = function(){   | interpreted |-->
};                              |    like     |      functionOne = function(){
                                ---------------      };

Зауважте, що частина декларацій із призначенням не піднімалася. Піднімається лише назва.

Але у випадку з деклараціями функцій також буде піднято весь функціональний орган :

functionTwo();              ---------------      function functionTwo() {
                            | is actually |      };
function functionTwo() {    | interpreted |-->
}                           |    like     |      functionTwo();
                            ---------------

Я привітаюсь за чітку інформацію про тему функції. Тепер моє запитання: яке з них буде першим оголошенням в ієрархії декларацій, чи змінне оголошення (functionOne), чи функція оголошення (functionTwo)?
Шараті РБ

91

Інші коментатори вже висвітлювали смислову різницю двох варіантів вище. Я хотів відзначити стилістичну різницю: Тільки варіація "призначення" може задати властивість іншого об'єкта.

Я часто будую модулі JavaScript з таким малюнком:

(function(){
    var exports = {};

    function privateUtil() {
            ...
    }

    exports.publicUtil = function() {
            ...
    };

    return exports;
})();

За такою схемою всі ваші загальнодоступні функції використовуватимуть призначення, а ваші приватні функції використовують декларацію.

(Зауважте також, що після присвоєння слід вимагати крапки з комою після заяви, тоді як декларація забороняє це.)


4
Наскільки я можу сказати, yuiblog.com/blog/2007/06/12/module-pattern є первинною посиланням на модель модуля. (Хоча ця стаття використовує var foo = function(){...}синтаксис навіть для приватних змінних.
Шон Макміллан

Насправді це не зовсім вірно в деяких старих версіях IE. ( function window.onload() {}була річ.)
Ри-

77

Ілюстрацією того, коли слід віддати перевагу першому методу другому, є те, коли потрібно уникати переосмислення попередніх визначень функції.

З

if (condition){
    function myfunction(){
        // Some code
    }
}

, це визначення myfunctionволі скасуватиме будь-яке попереднє визначення, оскільки воно буде здійснено в розбір часу.

Поки

if (condition){
    var myfunction = function (){
        // Some code
    }
}

виконує правильну роботу щодо визначення myfunctionлише тоді, коли conditionце виконано.


1
цей приклад хороший і близький до досконалості, але його можна вдосконалити. кращим прикладом може бути визначення var myFunc = null;за межами циклу або поза блоком if / elseif / else. Тоді ви можете умовно призначити різні функції одній і тій же змінній. У JS кращою умовою є присвоєння відсутнього значення нулю, а потім невизначеному. Тому слід спочатку оголосити myFunction нульовим, а потім призначити її пізніше, умовно.
Олександр Міллс

62

Важливою причиною є додавання однієї та лише однієї змінної як "Корінь" вашого простору імен ...

var MyNamespace = {}
MyNamespace.foo= function() {

}

або

var MyNamespace = {
  foo: function() {
  },
  ...
}

Існує безліч технік простору імен. Це стає важливішим із наявністю безлічі модулів JavaScript.

Також див. Як я можу оголосити простір імен у JavaScript?


3
Здається, ця відповідь була об'єднана в це питання з іншого питання, і формулювання може здатися крихітним, не пов'язаним з цим питанням. Чи можете ви відредагувати відповідь, щоб вона виглядала більш спрямована саме на це питання? (ще раз повторюю, це зовсім не ваша вина ... лише побічний ефект об'єднаного питання). Ви також можете її видалити, і я думаю, ви б зберегли свою репутацію. Або ви можете залишити його; оскільки він старий, це може не змінити великого значення.
Ендрю Барбер

55

Підняття - це дія інтерпретатора JavaScript щодо переміщення всіх оголошень змінних та функцій до вершини поточної області.

Однак піднімаються лише фактичні декларації. залишаючи завдання там, де вони є.

  • Функція змінної / Функції, оголошені всередині сторінки, є глобальними і можуть отримати доступ до будь-якої сторінки цієї сторінки.
  • Функції змінної / функції, оголошені всередині функції, мають локальну область застосування. означає, що вони доступні / доступні всередині функціонального тіла (сфери), вони недоступні поза тілом функції.

Змінна

Javascript називається мовою, що не вводиться. Це означає, що змінні Javascript можуть містити значення будь - якого типу даних . Javascript автоматично піклується про зміну типу змінної на основі значення / літералу, наданого під час виконання.

global_Page = 10;                                               var global_Page;      « undefined
    « Integer literal, Number Type.   -------------------       global_Page = 10;     « Number         
global_Page = 'Yash';                 |   Interpreted   |       global_Page = 'Yash'; « String
    « String literal, String Type.    «       AS        «       global_Page = true;   « Boolean 
var global_Page = true;               |                 |       global_Page = function (){          « function
    « Boolean Type                    -------------------                 var local_functionblock;  « undefined
global_Page = function (){                                                local_functionblock = 777 Number
    var local_functionblock = 777;                              };  
    // Assigning function as a data.
};  

Функція

function Identifier_opt ( FormalParameterList_opt ) { 
      FunctionBody | sequence of statements

      « return;  Default undefined
      « return 'some data';
}
  • Функції, задекларовані всередині сторінки, піднімаються до верхньої частини сторінки з глобальним доступом.
  • Функції, оголошені всередині функціонального блоку, піднімаються на верхню частину блоку.
  • Повернене значення функції за замовчуванням ' undefined ', значення змінної декларації за замовчуванням також 'undefined'

    Scope with respect to function-block global. 
    Scope with respect to page undefined | not available.

Декларація функції

function globalAccess() {                                  function globalAccess() {      
}                                  -------------------     }
globalAccess();                    |                 |     function globalAccess() { « Re-Defined / overridden.
localAccess();                     «   Hoisted  As   «         function localAccess() {
function globalAccess() {          |                 |         }
     localAccess();                -------------------         localAccess(); « function accessed with in globalAccess() only.
     function localAccess() {                              }
     }                                                     globalAccess();
}                                                          localAccess(); « ReferenceError as the function is not defined

Функція Вираз

        10;                 « literal
       (10);                « Expression                (10).toString() -> '10'
var a;                      
    a = 10;                 « Expression var              a.toString()  -> '10'
(function invoke() {        « Expression Function
 console.log('Self Invoking');                      (function () {
});                                                               }) () -> 'Self Invoking'

var f; 
    f = function (){        « Expression var Function
    console.log('var Function');                                   f ()  -> 'var Function'
    };

Функція, призначена змінній Приклад:

(function selfExecuting(){
    console.log('IIFE - Immediately-Invoked Function Expression');
}());

var anonymous = function (){
    console.log('anonymous function Expression');
};

var namedExpression = function for_InternalUSE(fact){
    if(fact === 1){
        return 1;
    }

    var localExpression = function(){
        console.log('Local to the parent Function Scope');
    };
    globalExpression = function(){ 
        console.log('creates a new global variable, then assigned this function.');
    };

    //return; //undefined.
    return fact * for_InternalUSE( fact - 1);   
};

namedExpression();
globalExpression();

javascript інтерпретується як

var anonymous;
var namedExpression;
var globalExpression;

anonymous = function (){
    console.log('anonymous function Expression');
};

namedExpression = function for_InternalUSE(fact){
    var localExpression;

    if(fact === 1){
        return 1;
    }
    localExpression = function(){
        console.log('Local to the parent Function Scope');
    };
    globalExpression = function(){ 
        console.log('creates a new global variable, then assigned this function.');
    };

    return fact * for_InternalUSE( fact - 1);    // DEFAULT UNDEFINED.
};

namedExpression(10);
globalExpression();

Ви можете перевірити декларацію функції, тест виразів за допомогою різних браузерів jsperf Test Runner


Функціональні класи конструктора ES5 : об'єкти функцій, створені за допомогою функції Function.prototype.bind

JavaScript розглядає функції як першокласні об'єкти, тому будучи об'єктом, ви можете призначити властивості функції.

function Shape(id) { // Function Declaration
    this.id = id;
};
    // Adding a prototyped method to a function.
    Shape.prototype.getID = function () {
        return this.id;
    };
    Shape.prototype.setID = function ( id ) {
        this.id = id;
    };

var expFn = Shape; // Function Expression

var funObj = new Shape( ); // Function Object
funObj.hasOwnProperty('prototype'); // false
funObj.setID( 10 );
console.log( funObj.getID() ); // 10

ES6 запровадив функцію стрілки : Вираз функції стрілки має коротший синтаксис, вони найкраще підходять для неметодних функцій, і їх не можна використовувати як конструктори.

ArrowFunction : ArrowParameters => ConciseBody.

const fn = (item) => { return item & 1 ? 'Odd' : 'Even'; };
console.log( fn(2) ); // Even
console.log( fn(3) ); // Odd

3
ах, ваша відповідь ... хіба це неоднозначно? добре написано, хоча так +1 для витрат і написання занадто багато інформації.
Датська

40

Я додаю власну відповідь лише тому, що всі інші ретельно покрили підйомну частину.

Я замислювався про те, який спосіб краще давно зараз, і завдяки http://jsperf.com тепер я знаю :)

введіть тут опис зображення

Оголошення функцій швидше, і ось що насправді має значення в веб-розробнику? ;)


8
Я б сказав, що ремонтопридатність є найважливішим аспектом більшості коду. Продуктивність важлива, але в більшості випадків IO, ймовірно, буде більшим вузьким місцем, ніж спосіб визначення ваших функцій. Однак є деякі проблеми, коли вам потрібен кожен біт продуктивності, який ви можете отримати, і це корисно в тих випадках. Тут також добре мати відповідь, яка чітко відповідає чітко визначеною частиною питання.
Річард Гарсайд

3
Ну, я виявив, що з Firefox навпаки. jsperf.com/sandytest
Sandeep Nayak

Лише оновлення, оскільки я перейшов на повний функціональний стиль програмування в JavaScript, я ніколи не використовую декларації, лише вирази функцій, щоб я міг ланцюжок і викликав свої функції за їх назвами змінних. Перевірте RamdaJS ...
Леон Габан

1
@SandeepNayak Я просто запускаю свій власний тест у Firefox 50.0.0 / Windows 7 0.0.0, і це насправді так само, як у Леона. Отже, якщо ваш тест правильний, я б зробив висновок, що тести jsperf не є показовими, і все залежить від вашого веб-переглядача та / або версії ОС, або від конкретного стану поточної машини в цей конкретний момент.
окламот

33

Оголошення функції та вираз функції, присвоєні змінній, поводяться однаково, як тільки встановлюється зв'язування.

Однак є різниця в тому, як і коли об'єкт функції насправді пов'язаний зі своєю змінною. Ця різниця пояснюється механізмом, який називається змінною підйомою в JavaScript.

В основному всі декларації функцій та декларації змінних піднімаються до верхньої частини функції, в якій відбувається декларування (саме тому ми говоримо, що JavaScript має область функцій ).

  • Коли декларація функції піднімається, функціональне тіло «слідує», тому коли оцінюється тіло функції, змінна негайно буде прив’язана до об’єкта функції.

  • Коли декларація змінної піднімається, ініціалізація не слідує, а "залишається позаду". Змінна ініціалізується undefinedна початку тіла функції та їй присвоюється значення у вихідному місці в коді. (Насправді йому буде присвоєно значення в кожному місці, де відбувається оголошення змінної з тим самим іменем.)

Порядок підйому також важливий: декларації функцій мають перевагу над оголошеннями змінних з тим самим іменем, а остання декларація функції має перевагу над попередніми оголошеннями функції з тим самим іменем.

Деякі приклади ...

var foo = 1;
function bar() {
  if (!foo) {
    var foo = 10 }
  return foo; }
bar() // 10

Мінлива fooпіднімається до верхньої частини функції, инициализируется undefined, так що !fooце true, так fooпризначено 10. fooПоза barсфери «S не має ніякого значення і незаймана.

function f() {
  return a; 
  function a() {return 1}; 
  var a = 4;
  function a() {return 2}}
f()() // 2

function f() {
  return a;
  var a = 4;
  function a() {return 1};
  function a() {return 2}}
f()() // 2

Оголошення функцій мають перевагу над оголошеннями змінних, а остання декларація функції "дотримується".

function f() {
  var a = 4;
  function a() {return 1}; 
  function a() {return 2}; 
  return a; }
f() // 4

У цьому прикладі aініціалізується об'єкт функції, отриманий в результаті оцінки другого оголошення оголошення, а потім присвоюється 4.

var a = 1;
function b() {
  a = 10;
  return;
  function a() {}}
b();
a // 1

Тут декларація функції піднімається спочатку, оголошуючи та ініціалізуючи змінну a. Далі присвоюється ця змінна 10. Іншими словами: призначення не присвоює зовнішній змінній a.


3
У вас є трохи дивний спосіб розміщення фіксаторів. Ви кодер Python? Схоже, ви намагаєтеся зробити Javascript схожим на Python. Боюся, це заплутано для інших людей. Якби мені довелося підтримувати ваш код JavaScript, я спершу передав би ваш код через автоматичний симпатичний принтер.
nalply

1
Відмінний пост. "Функція самовиконання" або "негайно викликається вираз функції" повинна бути достатньо простою для того, щоб побачити, і його переваги стилю не повинні ушкоджувати його посаду - що є точним і ідеально підсумовує "підйом". +1
Ricalsin

32

Перший приклад - це оголошення функції:

function abc(){}

Другий приклад - вираз функції:

var abc = function() {};

Основна відмінність полягає в тому, як вони піднімаються (піднімаються і декларуються). У першому прикладі вся декларація функції піднімається. У другому прикладі піднімається лише var 'abc', його значення (функція) буде невизначеним, а сама функція залишається в тому місці, яке було оголошено.

Простіше кажучи:

//this will work
abc(param);
function abc(){}

//this would fail
abc(param);
var abc = function() {}

Щоб детальніше вивчити цю тему, настійно рекомендую вам це посилання


1
Ваш приклад є таким самим, як і головна відповідь
GôTô

Основною причиною розміщення цієї відповіді було надання посилання внизу. Це було те, що мені бракувало, щоб повністю зрозуміти вищезазначене питання.
sla55er

1
Дуже круто, що ви хотіли поділитися посиланням. Але посилання на додаткову інформацію в SO повинні бути просто коментарем або до питання, або до вашої улюбленої відповіді. Дуже неоптимально захаращувати довгу, складну сторінку на зразок цієї з повторною інформацією, просто додати єдине корисне посилання в кінці її. Ні, ви не отримаєте балів повторень за надання посилання, але ви будете допомагати громаді.
XML

30

Що стосується вартості обслуговування коду, то названі функції більш переважні:

  • Незалежно від місця, де вони оголошені (але все ще обмежені за обсягом).
  • Більш стійкий до помилок, таких як умовна ініціалізація (ви все одно можете переохочувати, якщо хочете).
  • Код стає більш читабельним, виділяючи локальні функції окремо від функціональних можливостей області. Зазвичай в області використання функціональність виходить на перше місце, після чого йдуть оголошення локальних функцій.
  • У відладчику ви чітко побачите ім'я функції в стеку викликів замість функції "анонімний / оцінений".

Я підозрюю, що більше PROS для названих функцій дотримуються. І те, що зазначено як перевага названих функцій, є недоліком для анонімних.

Історично анонімні функції з'являлися через неможливість JavaScript як мови перелічити членів з названими функціями:

{
    member:function() { /* How do I make "this.member" a named function? */
    }
}

2
Є тест на підтвердження: blog.firsov.net/2010/01/… Тест на продуктивність JS - обсяг та названі функції - Аналітика
Саша Фірсов

25

З точки зору інформатики, ми говоримо про анонімні функції та іменовані функції. Я думаю, що найважливіша відмінність полягає в тому, що анонімна функція не пов'язана з іменем, звідси і назва анонімна функція. У JavaScript це об'єкт першого класу, який динамічно оголошується під час виконання.

Для отримання додаткової інформації про анонімні функції та обчислення лямбда, Вікіпедія - це хороший початок ( http://en.wikipedia.org/wiki/Anonymous_function ).


Станом на ES2015 (через шість з половиною років після опублікування вашої відповіді) обидві функції у запитанні названі.
TJ Crowder

25

Я використовую мінливий підхід у своєму коді з дуже конкретної причини, теорія якої висвітлювалася абстрактно вище, але приклад може допомогти таким людям, як я, з обмеженими знаннями JavaScript.

У мене є код, який мені потрібно виконати зі 160 незалежно розробленими брендами. Більшість коду є у спільних файлах, але специфічні для брендингу речі є в окремому файлі, по одному для кожного брендування.

Деякі бренди вимагають конкретних функцій, а деякі - ні. Іноді доводиться додавати нові функції, щоб робити нові конкретні речі. Я радий змінити кодований спільний доступ, але мені не хочеться змінювати всі 160 наборів файлів брендингу.

Використовуючи синтаксис змінної, я можу оголосити змінну (по суті вказівник на функцію) у спільному коді і або призначити тривіальну функцію заглушки, або встановити нуль.

Одна чи дві марки, які потребують конкретної реалізації функції, можуть визначити свою версію функції та призначити цю змінну, якщо вони хочуть, а решта нічого не роблять. Я можу перевірити нульову функцію, перш ніж виконувати її у спільному коді.

З коментарів людей вище, я вважаю, що можливо, можливо, переосмислити статичну функцію, але я думаю, що змінне рішення є приємним і зрозумілим.


25

Відповідь Грега досить хороша, але я все ж хотів би додати щось до цього, що я дізнався лише зараз, переглядаючи відео Дугласа Крокфорда .

Вираз функції:

var foo = function foo() {};

Операційна функція:

function foo() {};

Операційний функція - це лише скорочення для varоператора зі functionзначенням.

Тому

function foo() {};

розширюється до

var foo = function foo() {};

Що розширюється далі:

var foo = undefined;
foo = function foo() {};

І вони обоє підняті на вершину коду.

Знімок екрана з відео


7
Вибачте, але це неправильно - я не знаю, що Крокфорд намагається сказати на цьому слайді. Обидві декларації функції та змінної завжди піднімаються на вершину своєї області дії. Різниця полягає в тому, що присвоєння змінних (незалежно від того, призначаєте ви це рядок, булевий або функціональний), не піднімаються до верху, тоді як органи функцій (за допомогою функції оголошення) є.
Томас Гейман

Подивіться на ці приклади коду: gist.github.com/cyberthom/36603fbc20de8e04fd09
Thomas Heymann

23

Існує чотири примітних порівняння між двома різними деклараціями функцій, переліченими нижче.

  1. Наявність (обсяг) функції

Наступне працює, оскільки function add()передається в найближчий блок:

try {
  console.log("Success: ", add(1, 1));
} catch(e) {
  console.log("ERROR: " + e);
}

function add(a, b){
  return a + b;
}

Далі не працює, тому що змінна викликається перед призначенням змінної значення функції add.

try {
  console.log("Success: ", add(1, 1));
} catch(e) {
  console.log("ERROR: " + e);
}

var add=function(a, b){
  return a + b;
}

Вищевказаний код за функціоналом ідентичний наведеному нижче коду. Зауважте, що явне присвоєння add = undefinedє зайвим, тому що просто робити var add;точно так само, як var add=undefined.

var add = undefined;

try {
  console.log("Success: ", add(1, 1));
} catch(e) {
  console.log("ERROR: " + e);
}

add = function(a, b){
  return a + b;
}

Наступне не працює, тому що var add=витісняє function add().

try {
  console.log("Success: ", add(1, 1));
} catch(e) {
  console.log("ERROR: " + e);
}

var add=function add(a, b){
  return a + b;
}

  1. (функція). ім'я

Ім'я функції function thefuncname(){}є thefuncname , коли вона оголошена таким чином.

function foobar(a, b){}

console.log(foobar.name);

var a = function foobar(){};

console.log(a.name);

В іншому випадку, якщо функція оголошена як function(){}, функція .name є першою змінною, яка використовується для зберігання функції.

var a = function(){};
var b = (function(){ return function(){} });

console.log(a.name);
console.log(b.name);

Якщо для функції не встановлено змінних, то ім'ям функцій є порожній рядок ( "").

console.log((function(){}).name === "");

Нарешті, хоча змінна, якій функція призначена для початкового встановлення імені, послідовні змінні, встановлені для функції, не змінюють ім'я.

var a = function(){};
var b = a;
var c = b;

console.log(a.name);
console.log(b.name);
console.log(c.name);

  1. Продуктивність

У V8 та Spidermonkey Firefox від Google може бути кілька мікросекундних компіляцій JIST, але в кінцевому підсумку результат точно такий же. Щоб довести це, давайте вивчимо ефективність JSPerf на мікропоказках, порівнявши швидкість двох фрагментів порожнього коду. Тут ви знайдете тести JSPerf . І тут знайдено тест jsben.ch . Як бачите, помітна різниця є, коли їх не повинно бути. Якщо ви справді дивак із продуктивності, як я, то, можливо, варто більше ваших зусиль, намагаючись зменшити кількість змінних і функцій в області застосування, і особливо усунути поліморфізм (наприклад, використання однієї і тієї ж змінної для зберігання двох різних типів).

  1. Змінна змінність

Коли ви використовуєте varключове слово для оголошення змінної, ви можете перепризначити іншу величину подібній змінній.

(function(){
    "use strict";
    var foobar = function(){}; // initial value
    try {
        foobar = "Hello World!"; // new value
        console.log("[no error]");
    } catch(error) {
        console.log("ERROR: " + error.message);
    }
    console.log(foobar, window.foobar);
})();

Однак, коли ми використовуємо const-оператор, посилання на змінну стає незмінним. Це означає, що ми не можемо призначити новій величині змінної. Зауважте, однак, що це не робить вміст змінної незмінним: якщо ви це зробите const arr = [], то ви все ще можете це зробити arr[10] = "example". Лише робити щось на кшталт arr = "new value"або arr = []призведе до помилки, як показано нижче.

(function(){
    "use strict";
    const foobar = function(){}; // initial value
    try {
        foobar = "Hello World!"; // new value
        console.log("[no error]");
    } catch(error) {
        console.log("ERROR: " + error.message);
    }
    console.log(foobar, window.foobar);
})();

Цікаво, що якщо ми оголосимо змінну як function funcName(){}, то незмінність змінної така ж, як і декларування її var.

(function(){
    "use strict";
    function foobar(){}; // initial value
    try {
        foobar = "Hello World!"; // new value
        console.log("[no error]");
    } catch(error) {
        console.log("ERROR: " + error.message);
    }
    console.log(foobar, window.foobar);
})();

Що таке "найближчий блок"

"Найближчий блок" - це найближча "функція" (включаючи асинхронні функції, функції генератора та функції асинхронного генератора). Однак, що цікаво, function functionName() {}поводиться як var functionName = function() {}колись у блоці, що не закриває, до предметів поза зазначеним закриттям. Поспостерігайте.

  • Нормальний var add=function(){}

try {
  // typeof will simply return "undefined" if the variable does not exist
  if (typeof add !== "undefined") {
    add(1, 1); // just to prove it
    console.log("Not a block");
  }else if(add===undefined){ // this throws an exception if add doesn't exist
    console.log('Behaves like var add=function(a,b){return a+b}');
  }
} catch(e) {
  console.log("Is a block");
}
var add=function(a, b){return a + b}

  • Нормальний function add(){}

try {
  // typeof will simply return "undefined" if the variable does not exist
  if (typeof add !== "undefined") {
    add(1, 1); // just to prove it
    console.log("Not a block");
  }else if(add===undefined){ // this throws an exception if add doesn't exist
    console.log('Behaves like var add=function(a,b){return a+b}')
  }
} catch(e) {
  console.log("Is a block");
}
function add(a, b){
  return a + b;
}

  • Функція

try {
  // typeof will simply return "undefined" if the variable does not exist
  if (typeof add !== "undefined") {
    add(1, 1); // just to prove it
    console.log("Not a block");
  }else if(add===undefined){ // this throws an exception if add doesn't exist
    console.log('Behaves like var add=function(a,b){return a+b}')
  }
} catch(e) {
  console.log("Is a block");
}
(function () {
    function add(a, b){
      return a + b;
    }
})();

  • Заява (такий , як if, else, for, while, try/ catch/ finally, switch, do/ while, with)

try {
  // typeof will simply return "undefined" if the variable does not exist
  if (typeof add !== "undefined") {
    add(1, 1); // just to prove it
    console.log("Not a block");
  }else if(add===undefined){ // this throws an exception if add doesn't exist
    console.log('Behaves like var add=function(a,b){return a+b}')
  }
} catch(e) {
  console.log("Is a block");
}
{
    function add(a, b){
      return a + b;
    }
}

  • Функція стрілки с var add=function()

try {
  // typeof will simply return "undefined" if the variable does not exist
  if (typeof add !== "undefined") {
    add(1, 1); // just to prove it
    console.log("Not a block");
  }else if(add===undefined){ // this throws an exception if add doesn't exist
    console.log('Behaves like var add=function(a,b){return a+b}')
  }
} catch(e) {
  console.log("Is a block");
}
(() => {
    var add=function(a, b){
      return a + b;
    }
})();

  • Функція стрілки с function add()

try {
  // typeof will simply return "undefined" if the variable does not exist
  if (typeof add !== "undefined") {
    add(1, 1); // just to prove it
    console.log("Not a block");
  }else if(add===undefined){ // this throws an exception if add doesn't exist
    console.log('Behaves like var add=function(a,b){return a+b}')
  }
} catch(e) {
  console.log("Is a block");
}
(() => {
    function add(a, b){
      return a + b;
    }
})();


Це заслуговує на прийняту і найбільш схвальну відповідь
Аарон Джон Сабу

18

@EugeneLazutkin наводить приклад, коли він називає призначену функцію, яку можна використовуватиshortcut() як внутрішню посилання на себе. Джон Резіг наводить інший приклад - копіювання рекурсивної функції, призначеної іншому об’єкту, у своєму навчальному посібнику з вивчення розширеного JavaScript . Хоча про призначення функцій властивостям тут не суворо питання, я рекомендую активно спробувати підручник - запустіть код, натиснувши кнопку у верхньому правому куті та двічі клацніть на коді, щоб редагувати на свій смак.

Приклади з підручника: рекурсивні дзвінки в yell():

Якщо вилучено оригінальний об’єкт ніндзя, тести не вдаються. (стор. 13)

var ninja = { 
  yell: function(n){ 
    return n > 0 ? ninja.yell(n-1) + "a" : "hiy"; 
  } 
}; 
assert( ninja.yell(4) == "hiyaaaa", "A single object isn't too bad, either." ); 

var samurai = { yell: ninja.yell }; 
var ninja = null; 

try { 
  samurai.yell(4); 
} catch(e){ 
  assert( false, "Uh, this isn't good! Where'd ninja.yell go?" ); 
}

Якщо ви назвете функцію, яка буде називатися рекурсивно, тести пройдуть. (стор. 14)

var ninja = { 
  yell: function yell(n){ 
    return n > 0 ? yell(n-1) + "a" : "hiy"; 
  } 
}; 
assert( ninja.yell(4) == "hiyaaaa", "Works as we would expect it to!" ); 

var samurai = { yell: ninja.yell }; 
var ninja = {}; 
assert( samurai.yell(4) == "hiyaaaa", "The method correctly calls itself." );

17

Ще одна відмінність, яка не згадується в інших відповідях, полягає в тому, що якщо ви використовуєте функцію анонімного

var functionOne = function() {
    // Some code
};

і використовувати це як конструктор як в

var one = new functionOne();

тоді one.constructor.nameне буде визначено. Function.nameє нестандартним, але підтримується Firefox, Chrome, іншими браузерами, похідними від Webkit та IE 9+.

З

function functionTwo() {
    // Some code
}
two = new functionTwo();

можна отримати ім'я конструктора у вигляді рядка з two.constructor.name.


Ім'я в першому випадку не визначатиметься, оскільки його анонімна функція призначена змінній. Я думаю, що слово "анонім" було винайдено для речей, на яких не визначено їх ім'я :)
Ом Шанкар

У цьому прикладі два = нові перетворюються на глобальну функцію, оскільки жоден вар
Waqas Tahir

16

Перший (функція doSomething (x)) повинен бути частиною позначення об'єкта.

Другий один ( var doSomething = function(x){ alert(x);}) просто створює анонімну функцію і присвоюємо його змінної doSomething. Тож doSomething () викличе функцію.

Ви можете дізнатися, що таке декларація функції та вираження функції .

Оголошення функції визначає названу змінну функції, не вимагаючи присвоєння змінної. Оголошення функції відбуваються як окремі конструкції і не можуть бути вкладені в нефункціональні блоки.

function foo() {
    return 3;
}

ECMA 5 (13.0) визначає синтаксис як
функцію Ідентифікатор ( opt. FormalParameterList opt ) {FunctionBody}

У вищезгаданому стані ім'я функції видно в межах своєї сфери та сфери його батьківського використання (інакше це було б недосяжно).

І у виразі функції

Вираз функції визначає функцію як частину більшого синтаксису вираження (як правило, змінне призначення). Функції, визначені через вирази функцій, можуть бути названі або анонімними. Вирази функцій не повинні починатися з "функції".

// Anonymous function expression
var a = function() {
    return 3;
}

// Named function expression
var a = function foo() {
    return 3;
}

// Self-invoking function expression
(function foo() {
    alert("hello!");
})();

ECMA 5 (13.0) визначає синтаксис як
функцію Identifier opt (FormalParameterList opt ) {FunctionBody}


16

Я перелічу нижче відмінності:

  1. Декларація функції може бути розміщена в будь-якому місці коду. Навіть якщо він викликається до того, як визначення з'явиться в коді, воно виконується, коли декларація функції береться в пам'ять або таким чином, як вона піднімається, до того, як будь-який інший код на сторінці почне виконання.

    Подивіться на функцію нижче:

    function outerFunction() {
        function foo() {
           return 1;
        }
        return foo();
        function foo() {
           return 2;
        }
    }
    alert(outerFunction()); // Displays 2

    Це тому, що під час виконання це виглядає так:

    function foo() {  // The first function declaration is moved to top
        return 1;
    }
    function foo() {  // The second function declaration is moved to top
        return 2;
    }
    function outerFunction() {
        return foo();
    }
    alert(outerFunction()); //So executing from top to bottom,
                            //the last foo() returns 2 which gets displayed

    Вираз функції, якщо воно не визначене перед викликом, призведе до помилки. Крім того, тут саме визначення функції не переміщується вгорі і не призначається в пам'яті, як у деклараціях функції. Але змінна, якій ми присвоюємо функцію, піднімається вгору, а невизначена присвоюється їй.

    Ця ж функція, використовуючи вирази функцій:

    function outerFunction() {
        var foo = function() {
           return 1;
        }
        return foo();
        var foo = function() {
           return 2;
        }
    }
    alert(outerFunction()); // Displays 1

    Це тому, що під час виконання він виглядає так:

    function outerFunction() {
       var foo = undefined;
       var foo = undefined;
    
       foo = function() {
          return 1;
       };
       return foo ();
       foo = function() {   // This function expression is not reachable
          return 2;
       };
    }
    alert(outerFunction()); // Displays 1
  2. Небезпечно писати декларації функцій у нефункціональні блоки, як якщо б вони не були доступні.

    if (test) {
        function x() { doSomething(); }
    }
  3. Вираз з назви функцій, як наведений нижче, може не працювати в браузерах Internet Explorer до версії 9.

    var today = function today() {return new Date()}

1
@Arjun У чому проблема, якщо запитання було задано роками раніше? Відповідь приносить користь не лише ОП, але й потенційно всім користувачам ПП, незалежно від того, коли було поставлено запитання. А що не так у відповіді на запитання, на які вже є прийнята відповідь?
SantiBailors

1
@ Арджун, ти маєш зрозуміти, відповідаючи на старі питання - це не погано. Якби це було, SO мав би такий бар'єр. Уявіть, що в API є зміна (хоч і не в контексті цього питання), і хтось помічає її та надає відповідь із новим API, чи не слід цього допускати ?? До тих пір, поки відповідь не має сенсу і не належить тут, вона буде знімана і видалена автоматично. Вам не потрібно турбуватися з цим !!!!
Sudhansu Choudhary

15

Якщо ви використовуєте ці функції для створення об'єктів, ви отримаєте:

var objectOne = new functionOne();
console.log(objectOne.__proto__); // prints "Object {}" because constructor is an anonymous function

var objectTwo = new functionTwo();
console.log(objectTwo.__proto__); // prints "functionTwo {}" because constructor is a named function

Я не можу це відтворити. console.log(objectOne.__proto__);друкує "functionOne {}" на моїй консолі. Будь-які ідеї, чому це може бути так?
Майк

Я не можу також відтворити це.
daremkd

1
Це можливість вашого налагоджувача (відображати "клас" зареєстрованого об'єкта), і більшість з них можуть отримати ім'я навіть за анонімними виразами функцій в ці дні. До речі, вам слід уточнити, що між двома екземплярами функціональна різниця не існує.
Бергі

12

У світлі аргументу "названі функції відображаються у стеках стека" сучасні двигуни JavaScript насправді цілком здатні представляти анонімні функції.

Станом на це написання V8, SpiderMonkey, Chakra та Nitro завжди посилаються на названі функції за своїми іменами. Вони майже завжди посилаються на анонімну функцію за її ідентифікатором, якщо вона має її.

SpiderMonkey може з'ясувати ім'я анонімної функції, поверненої з іншої функції. Решта не можуть.

Якщо ви насправді дуже хотіли, щоб ваш ітератор і зворотні виклики успіху відображалися в сліді, ви можете назвати їх також ...

[].forEach(function iterator() {});

Але здебільшого не варто наголошувати на цьому.

Запряжка ( Fiddle )

'use strict';

var a = function () {
    throw new Error();
},
    b = function b() {
        throw new Error();
    },
    c = function d() {
        throw new Error();
    },
    e = {
        f: a,
        g: b,
        h: c,
        i: function () {
            throw new Error();
        },
        j: function j() {
            throw new Error();
        },
        k: function l() {
            throw new Error();
        }
    },
    m = (function () {
        return function () {
            throw new Error();
        };
    }()),
    n = (function () {
        return function n() {
            throw new Error();
        };
    }()),
    o = (function () {
        return function p() {
            throw new Error();
        };
    }());

console.log([a, b, c].concat(Object.keys(e).reduce(function (values, key) {
    return values.concat(e[key]);
}, [])).concat([m, n, o]).reduce(function (logs, func) {

    try {
        func();
    } catch (error) {
        return logs.concat('func.name: ' + func.name + '\n' +
                           'Trace:\n' +
                           error.stack);
        // Need to manually log the error object in Nitro.
    }

}, []).join('\n\n'));

V8

func.name: 
Trace:
Error
    at a (http://localhost:8000/test.js:4:11)
    at http://localhost:8000/test.js:47:9
    at Array.reduce (native)
    at http://localhost:8000/test.js:44:27

func.name: b
Trace:
Error
    at b (http://localhost:8000/test.js:7:15)
    at http://localhost:8000/test.js:47:9
    at Array.reduce (native)
    at http://localhost:8000/test.js:44:27

func.name: d
Trace:
Error
    at d (http://localhost:8000/test.js:10:15)
    at http://localhost:8000/test.js:47:9
    at Array.reduce (native)
    at http://localhost:8000/test.js:44:27

func.name: 
Trace:
Error
    at a (http://localhost:8000/test.js:4:11)
    at http://localhost:8000/test.js:47:9
    at Array.reduce (native)
    at http://localhost:8000/test.js:44:27

func.name: b
Trace:
Error
    at b (http://localhost:8000/test.js:7:15)
    at http://localhost:8000/test.js:47:9
    at Array.reduce (native)
    at http://localhost:8000/test.js:44:27

func.name: d
Trace:
Error
    at d (http://localhost:8000/test.js:10:15)
    at http://localhost:8000/test.js:47:9
    at Array.reduce (native)
    at http://localhost:8000/test.js:44:27

func.name: 
Trace:
Error
    at e.i (http://localhost:8000/test.js:17:19)
    at http://localhost:8000/test.js:47:9
    at Array.reduce (native)
    at http://localhost:8000/test.js:44:27

func.name: j
Trace:
Error
    at j (http://localhost:8000/test.js:20:19)
    at http://localhost:8000/test.js:47:9
    at Array.reduce (native)
    at http://localhost:8000/test.js:44:27

func.name: l
Trace:
Error
    at l (http://localhost:8000/test.js:23:19)
    at http://localhost:8000/test.js:47:9
    at Array.reduce (native)
    at http://localhost:8000/test.js:44:27

func.name: 
Trace:
Error
    at http://localhost:8000/test.js:28:19
    at http://localhost:8000/test.js:47:9
    at Array.reduce (native)
    at http://localhost:8000/test.js:44:27

func.name: n
Trace:
Error
    at n (http://localhost:8000/test.js:33:19)
    at http://localhost:8000/test.js:47:9
    at Array.reduce (native)
    at http://localhost:8000/test.js:44:27

func.name: p
Trace:
Error
    at p (http://localhost:8000/test.js:38:19)
    at http://localhost:8000/test.js:47:9
    at Array.reduce (native)
    at http://localhost:8000/test.js:44:27 test.js:42

SpiderMonkey

func.name: 
Trace:
a@http://localhost:8000/test.js:4:5
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1


func.name: b
Trace:
b@http://localhost:8000/test.js:7:9
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1


func.name: d
Trace:
d@http://localhost:8000/test.js:10:9
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1


func.name: 
Trace:
a@http://localhost:8000/test.js:4:5
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1


func.name: b
Trace:
b@http://localhost:8000/test.js:7:9
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1


func.name: d
Trace:
d@http://localhost:8000/test.js:10:9
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1


func.name: 
Trace:
e.i@http://localhost:8000/test.js:17:13
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1


func.name: j
Trace:
j@http://localhost:8000/test.js:20:13
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1


func.name: l
Trace:
l@http://localhost:8000/test.js:23:13
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1


func.name: 
Trace:
m</<@http://localhost:8000/test.js:28:13
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1


func.name: n
Trace:
n@http://localhost:8000/test.js:33:13
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1


func.name: p
Trace:
p@http://localhost:8000/test.js:38:13
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1

Чакра

func.name: undefined
Trace:
Error
   at a (http://localhost:8000/test.js:4:5)
   at Anonymous function (http://localhost:8000/test.js:47:9)
   at Global code (http://localhost:8000/test.js:42:1)


func.name: undefined
Trace:
Error
   at b (http://localhost:8000/test.js:7:9)
   at Anonymous function (http://localhost:8000/test.js:47:9)
   at Global code (http://localhost:8000/test.js:42:1)


func.name: undefined
Trace:
Error
   at d (http://localhost:8000/test.js:10:9)
   at Anonymous function (http://localhost:8000/test.js:47:9)
   at Global code (http://localhost:8000/test.js:42:1)


func.name: undefined
Trace:
Error
   at a (http://localhost:8000/test.js:4:5)
   at Anonymous function (http://localhost:8000/test.js:47:9)
   at Global code (http://localhost:8000/test.js:42:1)


func.name: undefined
Trace:
Error
   at b (http://localhost:8000/test.js:7:9)
   at Anonymous function (http://localhost:8000/test.js:47:9)
   at Global code (http://localhost:8000/test.js:42:1)


func.name: undefined
Trace:
Error
   at d (http://localhost:8000/test.js:10:9)
   at Anonymous function (http://localhost:8000/test.js:47:9)
   at Global code (http://localhost:8000/test.js:42:1)


func.name: undefined
Trace:
Error
   at e.i (http://localhost:8000/test.js:17:13)
   at Anonymous function (http://localhost:8000/test.js:47:9)
   at Global code (http://localhost:8000/test.js:42:1)


func.name: undefined
Trace:
Error
   at j (http://localhost:8000/test.js:20:13)
   at Anonymous function (http://localhost:8000/test.js:47:9)
   at Global code (http://localhost:8000/test.js:42:1)


func.name: undefined
Trace:
Error
   at l (http://localhost:8000/test.js:23:13)
   at Anonymous function (http://localhost:8000/test.js:47:9)
   at Global code (http://localhost:8000/test.js:42:1)


func.name: undefined
Trace:
Error
   at Anonymous function (http://localhost:8000/test.js:28:13)
   at Anonymous function (http://localhost:8000/test.js:47:9)
   at Global code (http://localhost:8000/test.js:42:1)


func.name: undefined
Trace:
Error
   at n (http://localhost:8000/test.js:33:13)
   at Anonymous function (http://localhost:8000/test.js:47:9)
   at Global code (http://localhost:8000/test.js:42:1)


func.name: undefined
Trace:
Error
   at p (http://localhost:8000/test.js:38:13)
   at Anonymous function (http://localhost:8000/test.js:47:9)
   at Global code (http://localhost:8000/test.js:42:1)

Нітро

func.name: 
Trace:
a@http://localhost:8000/test.js:4:22
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33

func.name: b
Trace:
b@http://localhost:8000/test.js:7:26
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33

func.name: d
Trace:
d@http://localhost:8000/test.js:10:26
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33

func.name: 
Trace:
a@http://localhost:8000/test.js:4:22
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33

func.name: b
Trace:
b@http://localhost:8000/test.js:7:26
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33

func.name: d
Trace:
d@http://localhost:8000/test.js:10:26
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33

func.name: 
Trace:
i@http://localhost:8000/test.js:17:30
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33

func.name: j
Trace:
j@http://localhost:8000/test.js:20:30
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33

func.name: l
Trace:
l@http://localhost:8000/test.js:23:30
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33

func.name: 
Trace:
http://localhost:8000/test.js:28:30
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33

func.name: n
Trace:
n@http://localhost:8000/test.js:33:30
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33

func.name: p
Trace:
p@http://localhost:8000/test.js:38:30
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33

12

У JavaScript є два способи створення функцій:

  1. Декларація функції:

    function fn(){
      console.log("Hello");
    }
    fn();

    Це дуже просто, зрозуміло, використовується в багатьох мовах і є стандартним для мов C сімейства. Ми оголосили функцію, яку визначили та виконали, викликавши її.

    Що ви повинні знати, це те, що функції насправді є об’єктами в JavaScript; внутрішньо ми створили об'єкт для вищезгаданої функції і дали йому ім'я, яке називається fn, або посилання на об'єкт зберігається у fn. Функції - це об'єкти в JavaScript; екземпляр функції насправді є об'єктом.

  2. Вираз функції:

    var fn=function(){
      console.log("Hello");
    }
    fn();

    JavaScript має першокласні функції, тобто створює функцію та призначає її змінній так само, як ви створюєте рядок або число та призначаєте її змінній. Тут змінна fn призначається функції. Причиною цього поняття є функції - об’єкти в JavaScript; fn вказує на об'єктний примірник вищевказаної функції. Ми ініціалізували функцію та призначили її змінній. Це не виконання функції та призначення результату.

Довідка: синтаксис оголошення функції JavaScript: var fn = function () {} vs function fn () {}


1
як щодо третього варіанту var fn = function fn() {...},?
chharvey

Привіт Чхарві, не впевнений у урському питанні, я думаю, ур говорить про вираження функції, про яке я вже згадував. Однак, якщо все-таки є якась плутанина, просто будьте більш детальною.
Anoop Rai

так, я питав про названий вираз функції. він схожий на ваш варіант №2, за винятком того, що функція має ідентифікатор. зазвичай цей ідентифікатор збігається зі змінною, якій йому призначено, але це не завжди так.
chharvey

1
Так Вираз з іменованою функцією схоже на мій варіант №2. Добре мати ідентифікатор не є обов'язковим, оскільки він не використовується. Кожного разу, коли ви будете виконувати вираз функції, ви будете використовувати змінну, що містить об'єкт функції. Ідентифікатор не призначений.
Anoop Rai

11

Обидва є різними способами визначення функції. Різниця полягає в тому, як браузер інтерпретує та завантажує їх у контекст виконання.

Перший випадок - це вирази функцій, які завантажуються лише тоді, коли інтерпретатор досягає цього рядка коду. Отже, якщо ви зробите це так, як описано нижче, ви отримаєте помилку, що functionOne не є функцією .

functionOne();
var functionOne = function() {
    // Some code
};

Причина полягає в тому, що в першому рядку функціяOne не присвоюється жодному значенню, а значить, воно не визначене. Ми намагаємось назвати це функцією, а значить, ми отримуємо помилку.

У другому рядку ми призначаємо посилання анонімної функції на функцію Один.

Другий випадок - це декларація функції, яка завантажується перед виконанням будь-якого коду. Отже, якщо вам подобається наступне, ви не отримаєте жодної помилки, оскільки декларація завантажується перед виконанням коду.

functionOne();
function functionOne() {
   // Some code
}

11

Про продуктивність:

Нові версії V8представили кілька оптимізацій під капотом, і так SpiderMonkey.

Зараз майже немає різниці між виразом і декларацією. Здається,
вираз функції зараз швидше .

Chrome 62.0.3202 Тест Chrome

FireFox 55 Тест на Firefox

Chrome Canary 63.0.3225 Тест Chrome Canary


AnonymousФункційні вирази, схоже, мають кращу ефективність щодо Namedвираження функції.


Firefox Chrome Canary ChromeFirefox названий_anonymous Канарка Chrome названа_анонімою Chrome з ім'ям_анонімний


1
Так, ця різниця настільки незначна, що, сподіваємось, розробники потурбуються самі про те, який підхід є більш досяжним для їх конкретних потреб, а не який може бути швидшим (ви отримаєте різні результати jsperf за кожної спроби залежно від того, що робить браузер - більшість завдань javascript не повинні стосуватися мікрооптимізації в цій мірі).
кальмар

@squidbe Різниці немає. Подивіться тут: jsperf.com/empty-tests-performance
Джек Гіффін

10

Вони досить схожі з деякими невеликими відмінностями. Перша - це змінна, яка призначається анонімній функції (Декларація про функцію), а друга - це нормальний спосіб створення функції в JavaScript (Декларація про анонімну функцію), і має використання, і мінуси, і плюси. :

1. Функція Вираз

var functionOne = function() {
    // Some code
};

Функція Вираз визначає функцію як частину синтаксису більшого виразу (як правило, змінне призначення). Функції, визначені за допомогою виразів функцій, можуть бути названі або анонімними. Вирази функцій не повинні починатися з "функції" (звідси дужки навколо прикладу самовикликання нижче).

Призначення змінної функції означає, що немає Хостинг, як ми знаємо, функції в JavaScript можуть підніматися, значить, їх можна викликати до того, як вони будуть оголошені, тоді як змінні потрібно оголосити перед тим, як отримати доступ до них, тому в цьому випадку ми не можемо отримати доступ до функції до того, де вона оголошена, також це може бути спосіб, коли ви пишете свої функції, для функцій, які повертають іншу функцію, такий вид декларації може мати сенс, також в ECMA6 і вище ви можете призначити цю функцію стрілки, яка можна використовувати для виклику анонімних функцій, також цей спосіб оголошення є кращим способом створення функцій конструктора в JavaScript.

2. Декларація функції

function functionTwo() {
    // Some code
}

Декларація функції визначає названу змінну функції, не вимагаючи присвоєння змінної. Декларації функції відбуваються як окремі конструкції і не можуть бути вкладені в нефункціональні блоки. Корисно думати про них як побратимів змінних декларацій. Так само, як декларації змінних повинні починатися з "var", так і декларації функцій повинні починатися з "function".

Це звичайний спосіб виклику функції в JavaScript, цю функцію можна викликати ще до того, як ви навіть заявите її, як у JavaScript всі функції отримують підняття, але якщо у вас є "суворо використовувати", це не піднімається, як очікувалося, це хороший спосіб викликати всі нормальні функції, які не є великими в рядках і не є функцією конструктора.

Також, якщо вам потрібна додаткова інформація про те, як працює підйомник у JavaScript, перейдіть за посиланням нижче:

https://developer.mozilla.org/en-US/docs/Glossary/Hoisting


1
...also this way of declaring is a better way to create Constructor functions in JavaScript, будь ласка, докладно, мені цікаво!
Карл Моррісон

Однією з причин є те, що всі вбудовані функції конструктора в JavaScript створені як ця функція Number () {[рідний код]}, і ви не повинні плутати їх із вбудованими, також посилання на це пізніше в цьому випадку безпечніше, і ви закінчите до більш акуратного коду, але не використовуючи підйом ...
Аліреза

8

Це лише два можливі способи декларування функцій, а по-другому, ви можете використовувати функцію перед оголошенням.


7

new Function()може використовуватися для передачі тіла функції в рядку. А отже, це можна використовувати для створення динамічних функцій. Також передача сценарію без виконання сценарію.

var func = new Function("x", "y", "return x*y;");
function secondFunction(){
   var result;
   result = func(10,20);
   console.log ( result );
}

secondFunction()

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