Var functionName = funkcija () {} vs funkcija functionName () {}

Neseniai pradėjau palaikyti kito vartotojo „JavaScript“ kodą. Ištaisysiu klaidas, pridedu funkcijas ir bandau tvarkyti kodą ir padaryti jį nuoseklesnę.

Ankstesnis kūrėjas naudoja du būdus, kaip deklaruoti funkcijas, ir aš negaliu suprasti, ar tai yra priežastis, ar ne.

Du būdai:

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

Kokios yra šių dviejų skirtingų metodų taikymo priežastys ir kokie yra kiekvieno iš jų privalumai ir trūkumai? Ar yra kažkas, ką galima padaryti vienu metodu, kurio negalima padaryti su kitu?

6296
03 дек. Richard Garside paklausė 03 d 2008-12-03 14:31 '08 at 14:31 pm 2008-12-03 14:31
@ 37 atsakymai
  • 1
  • 2

Skirtumas yra tas, kad functionOne Viena“ yra functionOne išraiška, todėl ji nustatoma tik tada, kai pasiekiama ši linija, o functionTwo yra funkcijų deklaracija ir yra nustatoma, kai tik įvyksta jo aplinkinė funkcija arba scenarijus (dėl kėlimo ).

Pavyzdžiui, išraiškos funkcija:

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

Tai taip pat reiškia, kad negalėsite sąlyginai apibrėžti funkcijų naudodami funkcijų deklaracijas:

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

Pirmiau aprašyta functionThree Trys“, neatsižvelgiant į test vertę, nebent use strict veiksmus, tokiu atveju jis tiesiog sukelia klaidą.

4669
03 дек. atsakymas, kurį pateikė Greg 03 dec. 2008-12-03 14:37 '08 at 14:37 pm 2008-12-03 14:37

Pirmiausia noriu pataisyti „Greg“: function abc(){} pat function abc(){} ribota; - pavadinimas abc yra apibrėžtas toje vietovėje, kurioje randamas šis apibrėžimas. Pavyzdys:

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

Antra, galite derinti abu stilius:

 var xyz = function abc(){}; 

xyz bus apibrėžta kaip įprasta, abc - neapibrėžta visose naršyklėse, bet „Internet Explorer“ - nesiremia jo apibrėžimu. Bet jis bus apibrėžtas jo kūno viduje:

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

Jei visose naršyklėse norite naudoti slapyvardžius, naudokite šio tipo skelbimus:

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

Tokiu atveju ir xyz ir abc yra to paties objekto slapyvardžiai:

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

Vienas iš įtikinamų priežasčių, kodėl naudojamasi kombinuotu stiliumi, yra funkcijų objektų atributas „name“ ( „Internet Explorer“ nepalaiko ). Iš esmės, kai apibrėžiate tokią funkciją

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

jo vardas automatiškai priskiriamas. Bet kai jį apibrėžiate kaip

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

jo pavadinimas yra tuščias - sukūrėme anoniminę funkciją ir priskyrėme tam tikrą kintamąjį.

Kita gera priežastis, kodėl naudokite kombinuotą stilių, yra trumpas vidinis pavadinimas, kad jį būtų galima nurodyti, suteikiant ilgą nesuderinamą išorinių naudotojų pavadinimą:

 // 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); // ... } 

Pirmiau pateiktame pavyzdyje mes galime padaryti tą patį su išoriniu pavadinimu, tačiau jis bus pernelyg sudėtingas (ir lėčiau).

(Kitas būdas kreiptis į save yra arguments.callee naudojimas, kuris vis dar yra gana ilgas ir nėra palaikomas griežtu režimu.)

Žemyn, „JavaScript“ abu pareiškimus tvarko skirtingai. Tai yra funkcijų deklaracija:

border=0
 function abc(){} 

abc čia apibrėžiama visur dabartinėje srityje:

 // We can call it here abc(); // Works // Yet, it is defined down there. function abc(){} // We can call it again abc(); // Works 

Be to, jis pakilo naudodamas return :

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

Tai yra funkcijos išraiška:

 var xyz = function(){}; 

xyz čia apibrėžiamas iš paskirties vietos:

 // We can't call it here xyz(); // UNDEFINED!!! // Now it is defined xyz = function(){} // We can call it here xyz(); // works 

Funkcijos deklaracija ir funkcijos išraiška yra tikroji priežastis, dėl kurios yra skirtumas, kurį parodė Gregas.

Pramogos:

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

Asmeniškai aš norėčiau deklaruoti „funkcijų išraišką“, nes tokiu būdu galiu kontroliuoti matomumą. Kai apibrėžiau tipo funkciją

 var abc = function(){}; 

Žinau, kad funkciją apibrėžiau vietoje. Kai apibrėžiau tipo funkciją

 abc = function(){}; 

Žinau, kad jį apibūdinau visame pasaulyje, nurodydama, kad aš neapibrėžiau abc bet kurioje regionų grandinėje. Šis apibrėžimo stilius yra stabilus net ir tada, kai jis naudojamas eval() . Nors apibrėžimas

 function abc(){}; 

priklauso nuo konteksto ir gali palikti jus įdomu, kur jis yra apibrėžtas, ypač eval() - Atsakymas: Tai priklauso nuo naršyklės.

1846 m
03 дек. Atsakymą pateikė Eugene Lazutkin 03 dec. 2008-12-03 20:43 '08, 8:43 pm 2008-12-03 20:43

Čia yra standartinių formų, kurios sukuria funkcijas, santrauka: (Iš pradžių parašyta kitam klausimui, bet pritaikyta po perėjimo prie kanoninio klausimo.)

Sąlygos:

Greitasis sąrašas:

  • Funkcijų deklaracija

  • „Anoniminė“ function Expression“ (kuri, nepaisant terminų, kartais sukuria funkcijas su pavadinimais)

  • Pavadinta function Expression“

  • Prieigos funkcijos iniciatorius (ES5 +)

  • Rodyklės funkcijos išraiška (ES2015 +) (kuri, kaip anoniminės funkcijos išraiškos, neturi aiškaus pavadinimo ir gali sukurti funkcijas su vardais)

  • Metodo deklaracija objektų iniciatoriuje (ES2015 +)

  • Konstruktorių ir metodų deklaracijos class (ES2015 +)

Funkcijų deklaracija

Pirmoji forma yra funkcijų deklaracija, kuri atrodo taip:

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

Funkcijos deklaracija yra skelbimas; tai nėra pareiškimas ar išraiška. Taigi jūs jo nesekate ; (nors ir nekenksmingas).

Funkcijos deklaracija apdorojama, kai vykdymas patenka į kontekstą, kuriame jis pasirodo prieš pradedant bet kokį žingsnio kodą. x sukurta funkcija priskiriama savo pavadinimui (pirmiau pateiktame pavyzdyje x ), ir šis pavadinimas yra toje vietoje, kurioje yra deklaracija.

Kadangi jis apdorojamas prieš bet kokį žingsnio kodą tame pačiame kontekste, galite tai padaryti:

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

Prieš „ES2015“, specifikacija neapima to, ką „JavaScript“ variklis turėtų daryti, jei įdėjote funkcijų deklaraciją valdymo struktūros viduje, pavyzdžiui, try , if , switch , while ir tt Pavyzdžiui:

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

Ir kadangi jie yra apdorojami prieš pradedant žingsnis po žingsnio kodą, sunku žinoti, ką daryti, kai jie yra valdymo struktūroje.

Nors tai nebuvo nurodyta prieš ES2015, tai buvo galiojantis išplėtimas funkcijų deklaracijoms blokuoti. Deja (ir neišvengiamai), skirtingi varikliai padarė skirtingus dalykus.

Pradedant nuo ES2015, specifikacijoje nurodoma, ką daryti. Iš tiesų, jame pateikiami trys atskiri veiksmai:

  1. Jei laisvo režimo nėra žiniatinklio naršyklėje, „JavaScript“ variklis turėtų daryti vieną dalyką.
  2. Jei interneto naršyklėje laisvas režimas, „JavaScript“ variklis turi daryti kažką kito.
  3. Jei griežto režimo (naršyklės ar ne), „JavaScript“ variklis turėtų daryti dar vieną dalyką.

Laisvų režimų taisyklės yra sudėtingos, tačiau griežtu režimu blokų funkcijų deklaracijos yra paprastos: jos yra vietinės prie bloko (jos turi blokų sritį, kuri taip pat yra nauja ES2015), ir jie eina į bloką. Taigi:

 "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) 

Išraiška „anoniminė“ function

Antroji bendra forma vadinama anonimine funkcija:

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

Kaip ir visos išraiškos, jis skaičiuojamas, kai pasiekiamas žingsnis po žingsnio.

ES5 sukuriama funkcija neturi pavadinimo (ji yra anoniminė). ES2015 atveju funkcija, kai tik įmanoma, priskiriamas pavadinimui, atsižvelgiant į kontekstą. Pirmiau pateiktame pavyzdyje pavadinimas bus y . Taip atsitinka, kai funkcija yra turto iniciatoriaus vertė. (Daugiau informacijos apie tai, kada tai atsitinka ir apie taisykles, rasite specifikacijoje „ SetFunctionName “ - jis rodomas visur.)

Pavadinta function Expression“

Trečioji forma yra išraiška su pavadinimu „NFE“:

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

Jo sukurta funkcija turi savo pavadinimą (šiuo atveju, w ). Kaip ir visos išraiškos, tai vertinama, kai ji pasiekiama naudojant laipsnišką kodo vykdymą. Funkcijos pavadinimas nėra įtrauktas į sritį, kurioje rodoma išraiška; pavadinimas priklauso pačiai funkcijai:

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

Atkreipkite dėmesį, kad „NFE“ dažnai yra klaidų šaltinis „JavaScript“ diegimui. Pavyzdžiui, „IE8“ ir ankstesnės versijos „NFE“ tvarko visiškai neteisingai , sukurdamos dvi skirtingas funkcijas dviem skirtingais laiko momentais. Ankstyvosios „Safari“ versijos taip pat turėjo problemų. Geros naujienos yra tai, kad dabartinėse naršyklės versijose (IE9 ir naujesnėse versijose, dabartinėje Safari) tokios problemos nebėra. (Deja, šio rašymo metu „IE8“ vis dar plačiai naudojamas, todėl vis dar problemiška naudoti „NFE“ su kodu internetu.)

Prieigos funkcijos iniciatorius (ES5 +)

Kartais funkcijos gali prasiskverbti nepastebimai; kaip apie prieigos funkcijas. Štai pavyzdys:

 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" 

Atminkite, kad naudodamas šią funkciją nenaudojau () ! Taip yra todėl, kad tai yra nuosavybės prieigos funkcija. Mes gauname ir nustatome nuosavybę įprastu būdu, bet užkulisiuose yra funkcija.

Prieigos funkcijas taip pat galite sukurti naudodami Object.defineProperty , Object.defineProperties ir mažiau žinomą antrąjį argumentą Object.create .

Rodyklės funkcijos išraiška (ES2015 +)

ES2015 suteikia mums rodyklės funkciją. Štai vienas pavyzdys:

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

Žr., Kas n => n * 2 yra paslėpta map() ? Tai yra funkcija.

Keletas dalykų apie rodyklių funkcijas:

  1. Jie neturi savo. Vietoj to jie uždaro this kontekstą, kuriame jie yra apibrėžti. (Jie taip pat yra arti arguments ir, prireikus, super .) Tai reiškia, kad this tokie, kaip jie, kur jie yra sukurti ir negali būti keičiami.

  2. Kaip jau minėjote, jūs nenaudojate raktinio žodžio function ; vietoj to naudojate => .

Pavyzdys n => n * 2 aukščiau yra viena iš jų formų. Jei turite kelis argumentus, kad galėtumėte perduoti funkciją, naudokite parenas:

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

(Atminkite, kad „ Array#map perduoda įrašą kaip pirmąjį argumentą ir indeksą kaip antrąjį.)

Abiem atvejais funkcijų kūnas yra tik išraiška; funkcijos grąžinimo vertė automatiškai bus šios išraiškos rezultatas (nenaudojate aiškios return ).

Jei darote daugiau nei vieną išraišką, naudokite {} ir aiškią return (jei norite grąžinti vertę), kaip įprasta:

 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)); 

Versija be {... } vadinama rodyklės funkcija su išraiška arba trumpu kūnu. (Taip pat: Trumpa rodyklės funkcija.) Funkcija su {... } apibrėžiančia kūną, yra rodyklės funkcija su funkcijos korpusu. (Taip pat: veiksmažodžio rodyklės funkcija.)

Metodo deklaracija objektų iniciatoriuje (ES2015 +)

ES2015 leidžia trumpesnę nuosavybės deklaracijos formą, kuri nurodo funkciją, vadinamą metodo apibrėžimu; tai atrodo taip:

 var o = { foo() { } }; 

beveik atitinka ES5 ir ankstesnes versijas:

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

Skirtumas (išskyrus verbiškumą) yra tas, kad metodas gali naudoti super , bet funkcija negali. Taigi, pavyzdžiui, jei turėjote objektą, kuris apibrėžė (sako) valueOf naudojant metodo sintaksę, jis galėtų naudoti super.valueOf() kad gautų Object.prototype.valueOf vertę, kuri turi būti grąžinta (prieš super.valueOf() kažką daryti) kažką kita su juo), o ES5 versija turėtų būti Object.prototype.valueOf.call(this) kad padarytų Object.prototype.valueOf.call(this) .

Tai taip pat reiškia, kad metodas turi nuorodą į objektą, kuriam jis buvo nustatytas, todėl, jei šis objektas yra laikinas (pvz., Object.assign jį į Object.assign kaip vieną iš originalių objektų), metodo sintaksė gali reikšti, kad objektas yra išsaugotas atminties, jei kitaip jį galėtų surinkti šiukšlių surinkėjas (jei „JavaScript“ variklis nepastebi šios situacijos ir neveikia, jei nė vienas iš metodų nenaudoja super ).

Konstruktorių ir metodų deklaracijos class (ES2015 +)

ES2015 suteikia mums class sintaksę, įskaitant deklaruotus konstruktorius ir metodus:

 class Person { constructor(firstName, lastName) { this.firstName = firstName; this.lastName = lastName; } getFullName() { return this.firstName + " " + this.lastName; } } 

Aukščiau yra dvi funkcijų deklaracijos: viena konstruktoriui, kuris yra pavadintas Person , ir getFullName getFullName , kuris yra funkcija, priskirta Person.prototype .

574
04 марта '14 в 16:35 2014-03-04 16:35 Atsakymą pateikė TJ Crowder kovo 04 '14, 16:35 2014-03-04 16:35

Kalbant apie pasaulinį kontekstą, tiek var ir FunctionDeclaration pareiškimai pabaigoje sukuria neištrinamą visuotinio objekto nuosavybę, tačiau abiejų reikšmė gali būti perrašyta.

Subtilus skirtumas tarp dviejų būdų yra tas, kad pradėjus Variable Instantiation procesą (iki faktinio kodo vykdymo), visi su var identifikatoriai bus inicijuoti undefined , o tie, kuriuos naudoja „ FunctionDeclaration bus pasiekiami nuo šiol, pavyzdžiui:

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

bar FunctionExpression “ priskyrimas atliekamas prieš vykdymą.

Funkcija „Deklaracija“ sukurta pasaulinė nuosavybė gali būti perrašyta be jokių problemų taip pat, kaip kintamojo vertė, pavyzdžiui:

  function test () {} test = null; 

Kitas akivaizdus skirtumas tarp jūsų dviejų pavyzdžių yra tas, kad pirmoji funkcija neturi pavadinimo, bet antroji - tai, kas gali būti tikrai naudinga derinant (pvz., Tikrinant skambučių steką).

Apie jūsų redaguotą pirmąjį pavyzdį ( foo = function() { alert('hello!'); }; ), Tai nedeklaruota užduotis, aš primygtinai rekomenduojame visada naudoti raktinį žodį var .

Priskiriant be var operatoriaus, jei atskaitos identifikatorius nerandamas taikymo grandinėje, jis taps nuimamu pasaulinio objekto turtu.

Be to, nedeklaruotos užduotys „ECMAScript 5“ griežtais režimais išmeta „ ReferenceError “.

A turi skaityti:

Pastaba : Šis atsakymas buvo sujungtas su kitu klausimu , kuriame pagrindinė OP abejonė ir klaidinga nuomonė buvo ta, kad su FunctionDeclaration deklaruoti identifikatoriai negali būti perrašyti, o ne taip.

137
08 авг. atsakymą pateikė CMS 08 rug. 2010-08-08 22:32 '10, 10:32, 2010-08-08 22:32

Du kodų fragmentai, kuriuos jūs įdėjote ten, veiks visais tikslais.

Tačiau elgesio skirtumas yra tas, kad pirmoji parinktis ( var functionOne = function() {} ) šią funkciją galima vadinti tik po šio kodo taško.

Antrame variante ( function functionTwo() ) funkcija yra prieinama pirmiau nurodytam kodui, kur deklaruojama funkcija.

Taip yra dėl to, kad pirmame variante funkcija priskiriama kintamajam foo vykdymo metu. Antroje funkcijoje šis identifikatorius priskirtas foo per analizę.

Papildoma techninė informacija

„JavaScript“ gali apibrėžti tris funkcijas.

  • Pirmajame fragmente rodoma funkcijos išraiška. Taip yra dėl to, kad funkcija „operatorius“ sukuria funkciją - šio operatoriaus rezultatas gali būti saugomas bet kuriame kintamajame ar objekte. Funkcijos išraiška yra tokia galinga. Funkcijos išraiška dažnai vadinama „anonimine funkcija“, nes ji neturėtų turėti pavadinimo,
  • Antrasis pavyzdys yra funkcijų deklaracija. . Norėdami sukurti funkciją, naudokite operatoriaus funkciją. Funkcija teikiama analizuojant ir gali būti vadinama visur šioje srityje. Vėliau galite išsaugoti jį kintamajame ar objekte.
  • Trečias būdas apibrėžti funkciją yra „Funkcija ()“ konstruktorius, kuris nėra rodomas pradiniame pranešime. Nerekomenduojama to naudoti, nes jis veikia taip pat kaip ir eval() , kuris turi savo problemų.
115
20 апр. atsakymą pateikė thomasrutter balandžio 20 d 2010-04-20 07:54 '10, 7:54, 2010-04-20 07:54

Gregas atsako į geriausią paaiškinimą

 functionTwo(); function functionTwo() { } 

Kodėl nėra klaidų? Mes visada mokėme, kad išraiškos vykdomos iš viršaus į apačią (??)

Nes:

Funkcijų deklaracijos ir kintamos deklaracijos visada perkeliamos ( hoisted ) į savo regiono viršų, naudojant „JavaScript“ vertėjo žodžius. Akivaizdu, kad jau egzistuoja funkciniai parametrai ir kalbos pavadinimai. ben cherry

Tai reiškia, kad toks kodas:

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

Atkreipkite dėmesį, kad dalis deklaracijų priskyrimo nebuvo iškelta. Pakeltas tik pavadinimas.

Tačiau funkcijų deklaracijų atveju taip pat bus iškeltas visos funkcijos kūnas:

 functionTwo(); --------------- function functionTwo() { | is actually | }; function functionTwo() { | interpreted |--> } | like | functionTwo(); --------------- 
96
09 авг. atsakymas suteiktas paprastam žmogui 09 rug . 2014-08-09 05:45 '14, 5:45 val. 2014-08-09 05:45

Kiti komentatoriai jau svarstė šių dviejų variantų semantinį skirtumą. Norėčiau atkreipti dėmesį į stilistinį skirtumą: tik „paskirties“ variantas gali nustatyti kito objekto nuosavybę.

Dažnai sukuriu „JavaScript“ modulius su šiuo modeliu:

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

Naudodami šį šabloną, jūsų viešosios funkcijos naudoja paskirties vietą, o jūsų privačios funkcijos naudoja skelbimą.

(Taip pat atkreipkite dėmesį, kad užduotyje turi būti po kablelio kabliataškis, o skelbimas draudžia.)

87
03 марта '11 в 22:19 2011-03-03 22:19 atsakė Sean McMillan kovo 03d. 11, 22:19 2011-03-03 22:19

Pavyzdys, kada geriau naudoti pirmąjį metodą antrajam atvejui yra tada, kai reikia vengti viršyti ankstesnių apibrėžčių funkcijas.

Su

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

ši apibrėžtis myfunction panaikins bet kokį ankstesnį apibrėžimą, nes ji bus vykdoma analizuojant.

Nors

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

teisingai atlieka mano myfunction tik tada, kai įvykdoma condition .

73
29 марта '13 в 16:26 2013-03-29 16:26 Mbengueo Asano atsakymas kovo 29 d. 13 d. 16:26 2013-03-29 16:26

Svarbi priežastis yra vieno ir tik vieno kintamojo pridėjimas kaip jūsų vardų srities „šaknis“ ...

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

arba

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

Vardų erdvėje yra daug metodų. Tai tampa vis svarbesnė, kai yra daug „JavaScript“ modulių.

Taip pat žr. Kaip paskelbti vardų sritį javascript'e?

59
08 авг. atsakymą pateikė Rob 08 rug. 2010-08-08 22:44 '10, 10:44 PM 2010-08-08 22:44

Hoisting - это действие интерпретаторов JavaScript для перемещения всех объявлений переменных и функций в начало текущей объем.