Var functionName = function () {} срещу функция functionName () {}

Наскоро започнах да поддържам JavaScript кода на друг потребител. Поправям грешки, добавям функции и също така се опитвам да подредя кода и да го направя по-последователен.

Предишният разработчик използва два начина за деклариране на функции и не мога да разбера дали има причина за това или не.

Два начина са:

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

Какви са причините за използването на тези два различни метода и какви са предимствата и недостатъците на всяка от тях? Има ли нещо, което може да се направи с един метод, който не може да се направи с друг?

6296
03 дек. Ричард Гарсайд попита 03 декември 2008-12-03 14:31 '08 в 14:31 ч. 2008-12-03 14:31
@ 37 отговора
  • 1
  • 2

Разликата е, че functionOne е израз на функция и затова се определя само когато тази линия е достигната, докато functionTwo е декларация на функция и се определя веднага щом бъде изпълнена заобикалящата му функция или скрипт (поради повдигане ).

Например функцията за изразяване:

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

Това също означава, че не можете да дефинирате условно функции, използващи декларации за функции:

 if (test) { // Error or misbehavior function functionThree() { doSomething(); } } 

Посоченото по-горе дефинира functionThree независимо от стойността на test - освен ако не се use strict действие, в който случай просто предизвиква грешка.

4669
03 дек. отговор, даден от Greg 03 dec. 2008-12-03 14:37 '08 в 14:37 ч. 2008-12-03 14:37

Първо искам да поправя Грег: 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" 

Една от убедителните причини за използването на комбинирания стил е атрибутът "име" за функционални обекти ( не се поддържа от 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 обработва и двете твърдения по различен начин. Това е декларация за функция:

border=0
 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 за 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() - Отговор: Зависи от браузъра.

1846
03 дек. Отговорът е даден от Евгений Лазуткин 03 декември. 2008-12-03 20:43 '08 в 20:43 2008-12-03 20:43

Ето обобщение на стандартните форми, които създават функции: (Първоначално написани за друг въпрос, но адаптирани след прехода към каноничния въпрос.)

Срок на изпълнение:

Бърз списък:

  • Декларация на функцията

  • "Анонимна" function Expression (която, въпреки термина, понякога създава функции с имена)

  • Наименувана function Expression

  • Инициализатор на функцията за достъп (ES5 +)

  • Израз на функцията стрелка (ES2015 +) (която, подобно на анонимни функционални изрази, не съдържа изрично име и може да създава функции с имена)

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

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

Декларация на функцията

Първата форма е декларация за функция, която изглежда така:

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

Декларацията за функция е реклама; това не е изявление или израз. Така че не го следвате ; (въпреки че е безвреден).

Декларацията на функцията се обработва, когато изпълнението влезе в контекста, в който се появява, преди да се изпълни код на стъпка. Функцията, която създава, има собствено име ( x в примера по-горе) и това име се поставя в областта, в която се появява декларацията.

Тъй като се обработва преди код на стъпка в същия контекст, можете да направите нещо подобно:

 x(); // Works even though it 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 not in the same block) 

Изразът "анонимен" function

Втората обща форма се нарича анонимна функция:

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

Както всички изрази, той се изчислява при достигане на стъпка по стъпка изпълнение на код.

В ES5 създадената функция няма име (тя е анонимна). В ES2015 функцията се присвоява на име, когато е възможно, като се изважда от контекста. В примера по-горе името ще бъде y . Това се случва, когато функцията е стойността на инициализатора на свойствата. (За повече информация за това, кога това се случва и за правилата, намерете SetFunctionName в спецификацията - тя се появява навсякъде.)

Наименувана function Expression

Третата форма е израз с назована функция ("NFE"):

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

Функцията, която създава, има собствено име (в този случай w ). Подобно на всички изрази, това се оценява, когато се постига със стъпка по стъпка изпълнение на код. Името на функцията не се добавя към областта, в която се появява изразът; името е в обхвата на самата функция:

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

Моля, обърнете внимание, че NFEs често са източник на грешки за внедряването на 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 контекст, в който са дефинирани. (Те също са близки до arguments и, където е уместно, super .) Това означава, че 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.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 за getFullName , която е функция, присвоена на Person.prototype .

574
04 марта '14 в 16:35 2014-03-04 16:35 Отговор е даден от TJ Crowder 04 март 2014 г. в 16:35 2014-03-04 16:35

Като говорим за глобален контекст, и операторите var и FunctionDeclaration в края създават свойство, което не може да бъде изтрито, за глобалния обект, но стойността на двете може да бъде презаписана.

Тънкото различие между двата начина е, че когато процесът на Instantiation на променлива е стартиран (преди действителното изпълнение на кода), всички идентификатори, декларирани с var ще бъдат инициализирани с undefined , а тези, използвани от FunctionDeclaration ще бъдат достъпни отсега нататък, например:

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

Присвояването на bar FunctionExpression се извършва преди изпълнение.

Глобалното свойство, създадено от FunctionDeclaration може да бъде презаписано без никакви проблеми по същия начин, както стойността на променлива, например:

  function test () {} test = null; 

Друга очевидна разлика между вашите два примера е, че първата функция няма име, но втората я има, което може да бъде наистина полезно при дебъгване (например проверка на стека на повиквания).

За редактирания ви първи пример ( foo = function() { alert('hello!'); }; ), Това е недекларирана задача, силно препоръчвам винаги да използвате ключовата дума var .

Когато е зададен без оператор var , ако идентификаторът на референтния код не е намерен в веригата на обхвата, той ще се превърне в преносимо свойство на глобалния обект.

В допълнение, недекларираните задачи прехвърлят ReferenceError на ECMAScript 5 в Strict Mode .

А трябва да се чете:

Забележка : Този отговор е комбиниран от друг въпрос , в който основното съмнение и погрешно схващане от ОП е, че идентификаторите, декларирани с FunctionDeclaration не могат да бъдат презаписани, което не е така.

137
08 авг. отговорът се дава от CMS 08 авг. 2010-08-08 22:32 '10 в 10:32 2010-08-08 22:32

Двата кодови фрагмента, които поставите там, ще се държат еднакво за почти всички цели.

Разликата в поведението обаче е, че с първата опция ( var functionOne = function() {} ) тази функция може да бъде извикана само след тази точка в кода.

Във втория вариант ( function functionTwo() ), функцията е достъпна за кода, който се изпълнява по-горе, където функцията е декларирана.

Това се дължи на факта, че в първия вариант функцията е присвоена на променливата foo по време на изпълнение. Във втората функция този идентификатор се присвоява на foo по време на разбор.

Допълнителна техническа информация

JavaScript има три начина за дефиниране на функции.

  • В първия отрязък се показва функционалният израз. Това се дължи на използването на оператор "функция" за създаване на функция - резултатът от този оператор може да бъде съхранен във всяка променлива или обект. Функционалният израз е толкова мощен. Функционалният израз често се нарича "анонимна функция", защото не трябва да има име,
  • Вторият пример е декларация за функция. , За да създадете функция, използвайте оператора "функция". Функцията се предоставя по време на синтактичния анализ и може да бъде извикана навсякъде в тази област. Можете да го запазите по-късно в променлива или обект.
  • Третият начин за дефиниране на функция е "Function ()" конструктор, който не е показан в оригиналното съобщение. Не се препоръчва да се използва това, тъй като работи по същия начин като eval() , който има свои собствени проблеми.
115
20 апр. отговорът беше даден от thomasrutter Apr 20 2010-04-20 07:54 '10 в 7:54 2010-04-20 07:54

Грег отговаря на най-доброто обяснение

 functionTwo(); function functionTwo() { } 

Защо няма грешки? Винаги сме научавали, че изразите се изпълняват отгоре надолу (??)

Защото:

Декларациите за функции и декларациите на променливите винаги се преместват ( hoisted ) невидимо в горната част на техния регион, използвайки интерпретатора на JavaScript. Функционални параметри и имена на езици очевидно вече съществуват. ben cherry

Това означава, че такъв код:

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

Моля, имайте предвид, че част от възлагането на декларации не е повдигната. Само името е повдигнато.

Но в случай на декларации за функции, тялото на цялата функция също ще бъде повишено:

 functionTwo(); --------------- function functionTwo() { | is actually | }; function functionTwo() { | interpreted |--> } | like | functionTwo(); --------------- 
96
09 авг. отговорът е даден просто_хуман 09 август. 2014-08-09 05:45 '14 в 5:45 ч. 2014-08-09 05:45

Други коментатори вече са взели предвид семантичната разлика между двете изброени по-горе възможности. Бих искал да посоча една стилистична разлика: само вариация на "дестинация" може да установи свойството на друг обект.

Често създавам JavaScript модули с този модел:

 (function(){ var exports = {}; function privateUtil() { ... } exports.publicUtil = function() { ... }; return exports; })(); 

Използвайки този шаблон, вашите публични функции ще използват дестинацията, докато вашите лични функции ще използват рекламата.

(Обърнете внимание също, че заданието трябва да съдържа точка и запетая след инструкцията, докато съобщението го забранява.)

87
03 марта '11 в 22:19 2011-03-03 22:19 отговори на Sean McMillan на 03 март '11 в 22:19 2011-03-03 22:19

Илюстрация за това кога е по-добре да се използва първият метод за второто е, когато трябва да се избегне преодоляването на функциите на предишните дефиниции.

C

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

тази дефиниция myfunction замени предишното определение, тъй като тя ще бъде изпълнена по време на синтактичния анализ.

докато

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

прави правилната работа за дефиниране на myfunction само когато condition се изпълнява.

73
29 марта '13 в 16:26 2013-03-29 16:26 Отговор, даден от Mbengue Assane на 29 март'13 в 16:26 2013-03-29 16:26

Важна причина е добавянето на една и само една променлива като „корен“ на вашето пространство от имена ...

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

или

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

Има много методи за пространството от имена. Това става по-важно с многото налични JavaScript модули.

Вижте също.Как да обявим пространството за имена в javascript?

59
08 авг. отговорът се дава от Rob 08 aug. 2010-08-08 22:44 '10 в 22:44 2010-08-08 22:44

Издигането е действие на интерпретатора на JavaScript за преместване на всички декларации на променливи и функции в началото на текущия обхват.