Jeg har nylig begynt å vedlikeholde noen andres JavaScript-kode. Jeg fikser feil, legger til funksjoner og prøver også å rydde opp i koden og gjøre den mer konsistent.
Den forrige utvikleren bruker to måter å erklære funksjoner på, og jeg kan ikke finne ut om det er en grunn til det eller ikke.
De to måtene er:
var functionOne = function() {
// Some code
};
function functionTwo() {
// Some code
}
Hva er grunnene til å bruke disse to forskjellige metodene, og hva er fordelene og ulempene med hver av dem? Er det noe som kan gjøres med den ene metoden som ikke kan gjøres med den andre?
Forskjellen er at functionOne
er et funksjonsuttrykk og derfor bare defineres når den linjen nås, mens functionTwo
er en funksjonsdeklarasjon og defineres så snart den omkringliggende funksjonen eller skriptet utføres (på grunn av hoisting).
For eksempel et funksjonsuttrykk:
// TypeError: functionOne is not a function
functionOne();
var functionOne = function() {
console.log("Hello!");
};
Og en funksjonsdeklarasjon:
// Outputs: "Hello!"
functionTwo();
function functionTwo() {
console.log("Hello!");
}
Dette betyr også at du ikke kan definere funksjoner betinget ved hjelp av funksjonsdeklarasjoner:
if (test) {
// Error or misbehavior
function functionThree() { doSomething(); }
}
Ovennevnte definerer faktisk functionThree
uavhengig av test
's verdi, med mindre use strict
er i kraft, i hvilket tilfelle det bare reiser en feil.
Først vil jeg korrigere Greg: function abc(){}
er også scoped — navnet abc
er definert i omfanget der denne definisjonen finnes. Eksempel:
function xyz(){
function abc(){};
// abc is defined here...
}
// ...but not here
For det andre er det mulig å kombinere begge stilene:
var xyz = function abc(){};
xyz
kommer til å bli definert som vanlig, abc
er udefinert i alle nettlesere, men Internet Explorer — ikke stole på at den er definert. Men det vil bli definert inne i kroppen:
var xyz = function abc(){
// xyz is visible here
// abc is visible here
}
// xyz is visible here
// abc is undefined here
Hvis du vil ha aliasfunksjoner i alle nettlesere, bruker du denne typen erklæring:
function abc(){};
var xyz = abc;
I dette tilfellet er både xyz
og abc
alias for det samme objektet:
console.log(xyz === abc); // prints "true"
En overbevisende grunn til å bruke den kombinerte stilen er attributtet "name" for funksjonsobjekter (støttes ikke av Internet Explorer). I utgangspunktet når du definerer en funksjon som
function abc(){};
console.log(abc.name); // prints "abc"
blir navnet automatisk tildelt. Men når du definerer den som
var abc = function(){};
console.log(abc.name); // prints ""
er navnet tomt; vi opprettet en anonym funksjon og tilordnet den til en variabel.
En annen god grunn til å bruke den kombinerte stilen er å bruke et kort internt navn for å referere til seg selv, samtidig som det gir et langt navn som ikke er i konflikt for eksterne brukere:
// 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);
// ...
}
I eksemplet ovenfor kan vi gjøre det samme med et eksternt navn, men det blir for uhåndterlig (og tregere).
*(En annen måte å referere til seg selv på er å bruke arguments.callee
, som fremdeles er relativt langt, og som ikke støttes i den strenge modusen).
Innerst inne behandler JavaScript begge uttalelsene forskjellig. Dette er en funksjonserklæring:
function abc(){}
abc
er her definert overalt i det gjeldende omfanget:
// We can call it here
abc(); // Works
// Yet, it is defined down there.
function abc(){}
// We can call it again
abc(); // Works
Det heises også gjennom en return
-setning:
// We can call it here
abc(); // Works
return;
function abc(){}
Dette er et funksjonsuttrykk:
var xyz = function(){};
xyz
er her definert fra tildelingspunktet:
// We can't call it here
xyz(); // UNDEFINED!!!
// Now it is defined
xyz = function(){}
// We can call it here
xyz(); // works
Funksjonsdeklarasjon vs. funksjonsuttrykk er den virkelige grunnen til at det er en forskjell demonstrert av Greg.
Et morsomt faktum:
var xyz = function abc(){};
console.log(xyz.name); // Prints "abc"
Personlig foretrekker jeg "function expression"-erklæringen fordi jeg på denne måten kan kontrollere synligheten. Når jeg definerer funksjonen som
var abc = function(){};
vet jeg at jeg har definert funksjonen lokalt. Når jeg definerer funksjonen som
abc = function(){};
vet jeg at jeg definerte den globalt, forutsatt at jeg ikke definerte abc
noe sted i kjeden av scopes. Denne definisjonsstilen er spenstig selv når den brukes inne i eval()
. Mens definisjonen
function abc(){};
avhenger av konteksten og kan la deg gjette hvor den faktisk er definert, spesielt når det gjelder eval()
, er svaret: Det avhenger av nettleseren.
I datavitenskapelige termer snakker vi om anonyme funksjoner og navngitte funksjoner. Jeg tror den viktigste forskjellen er at en anonym funksjon ikke er bundet til et navn, derav navnet anonym funksjon. I JavaScript er det et førsteklasses objekt som deklareres dynamisk ved kjøretid.
For mer informasjon om anonyme funksjoner og lambdakalkulasjon er Wikipedia en god start (http://en.wikipedia.org/wiki/Anonymous_function).